diff options
428 files changed, 8014 insertions, 2712 deletions
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 fd8ddbcf3809..6c8af39015f5 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -59,6 +59,10 @@ public interface JobSchedulerInternal { */ void reportAppUsage(String packageName, int userId); + /** @return {@code true} if the app is considered buggy from JobScheduler's perspective. */ + boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName, + int timeoutBlameUserId, @NonNull String timeoutBlamePackageName); + /** * @return {@code true} if the given notification is associated with any user-initiated jobs. */ diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java index 8a5d09404ebb..071707059f2d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java @@ -123,21 +123,21 @@ class JobNotificationCoordinator { if (oldDetails == null) { if (jobStatus.startedAsUserInitiatedJob) { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_initial_setNotification_call_required", + "job_scheduler.value_cntr_w_uid_initial_set_notification_call_required", jobStatus.getUid()); } else { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_initial_setNotification_call_optional", + "job_scheduler.value_cntr_w_uid_initial_set_notification_call_optional", jobStatus.getUid()); } } else { if (jobStatus.startedAsUserInitiatedJob) { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_required", + "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_required", jobStatus.getUid()); } else { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_optional", + "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_optional", jobStatus.getUid()); } if (oldDetails.notificationId != notificationId) { @@ -145,7 +145,7 @@ class JobNotificationCoordinator { removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED, jobStatus); Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_setNotification_changed_notification_ids", + "job_scheduler.value_cntr_w_uid_set_notification_changed_notification_ids", jobStatus.getUid()); } } @@ -200,7 +200,10 @@ class JobNotificationCoordinator { // No more jobs using this notification. Apply the final job stop policy. // If the user attempted to stop the job/app, then always remove the notification // so the user doesn't get confused about the app state. + // Similarly, if the user background restricted the app, remove the notification so + // the user doesn't think the app is continuing to run in the background. if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE + || stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION || stopReason == JobParameters.STOP_REASON_USER) { mNotificationManagerInternal.cancelNotification( packageName, packageName, details.appUid, details.appPid, /* tag */ null, 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 cbc9263a2c3d..f99bcf144b91 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -322,16 +322,25 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; private static final String QUOTA_TRACKER_SCHEDULE_LOGGED = ".schedulePersisted out-of-quota logged"; + private static final String QUOTA_TRACKER_TIMEOUT_UIJ_TAG = "timeout-uij"; + private static final String QUOTA_TRACKER_TIMEOUT_EJ_TAG = "timeout-ej"; + private static final String QUOTA_TRACKER_TIMEOUT_REG_TAG = "timeout-reg"; + private static final String QUOTA_TRACKER_TIMEOUT_TOTAL_TAG = "timeout-total"; + private static final String QUOTA_TRACKER_ANR_TAG = "anr"; private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category( ".schedulePersisted()"); private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category( ".schedulePersisted out-of-quota logged"); - private static final Categorizer QUOTA_CATEGORIZER = (userId, packageName, tag) -> { - if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { - return QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED; - } - return QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED; - }; + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ = + new Category(QUOTA_TRACKER_TIMEOUT_UIJ_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ = + new Category(QUOTA_TRACKER_TIMEOUT_EJ_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_REG = + new Category(QUOTA_TRACKER_TIMEOUT_REG_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL = + new Category(QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_ANR = new Category(QUOTA_TRACKER_ANR_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_DISABLED = new Category("disabled"); /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list @@ -493,10 +502,18 @@ public class JobSchedulerService extends com.android.server.SystemService } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: + case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: case Constants.KEY_API_QUOTA_SCHEDULE_COUNT: case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS: case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT: case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS: if (!apiQuotaScheduleUpdated) { mConstants.updateApiQuotaConstantsLocked(); updateQuotaTracker(); @@ -583,10 +600,26 @@ public class JobSchedulerService extends com.android.server.SystemService @VisibleForTesting void updateQuotaTracker() { - mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS); + mQuotaTracker.setEnabled( + mConstants.ENABLE_API_QUOTAS || mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC); mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, mConstants.API_QUOTA_SCHEDULE_COUNT, mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_REG, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_ANR, + mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); } /** @@ -616,6 +649,8 @@ public class JobSchedulerService extends com.android.server.SystemService "conn_low_signal_strength_relax_frac"; private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = "prefetch_force_batch_relax_threshold_ms"; + // This has been enabled for 3+ full releases. We're unlikely to disable it. + // TODO(141645789): remove this flag private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count"; private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms"; @@ -623,6 +658,22 @@ public class JobSchedulerService extends com.android.server.SystemService "aq_schedule_throw_exception"; private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = "aq_schedule_return_failure"; + private static final String KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC = + "enable_execution_safeguards_udc"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = + "es_u_timeout_uij_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = + "es_u_timeout_ej_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = + "es_u_timeout_reg_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = + "es_u_timeout_total_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + "es_u_timeout_window_ms"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = + "es_u_anr_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + "es_u_anr_window_ms"; private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = "runtime_free_quota_max_limit_ms"; @@ -662,6 +713,17 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; + private static final boolean DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + // EJs have a shorter timeout, so set a higher limit for them to start with. + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 5; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 3; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 10; + private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + 24 * HOUR_IN_MILLIS; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = 3; + private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + 6 * HOUR_IN_MILLIS; @VisibleForTesting public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS; @VisibleForTesting @@ -774,6 +836,55 @@ public class JobSchedulerService extends com.android.server.SystemService public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT; + /** + * Whether to enable the execution safeguards added in UDC. + */ + public boolean ENABLE_EXECUTION_SAFEGUARDS_UDC = DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC; + /** + * The maximum number of times an app can have a user-iniated job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT; + /** + * The maximum number of times an app can have an expedited job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT; + /** + * The maximum number of times an app can have a regular job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT; + /** + * The maximum number of times an app can have jobs time out before the system + * attempts to restrict most of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT; + /** + * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT}, + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT}, + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT}, and + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT} should be evaluated over. + */ + public long EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS; + + /** + * The maximum number of times an app can ANR from JobScheduler's perspective before + * JobScheduler will attempt to restrict the app. + */ + public int EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT; + /** + * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_ANR_COUNT} + * should be evaluated over. + */ + public long EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS; + /** The maximum amount of time we will let a job run for when quota is "free". */ public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; @@ -915,6 +1026,9 @@ public class JobSchedulerService extends com.android.server.SystemService private void updateApiQuotaConstantsLocked() { ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS); + ENABLE_EXECUTION_SAFEGUARDS_UDC = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC); // Set a minimum value on the quota limit so it's not so low that it interferes with // legitimate use cases. API_QUOTA_SCHEDULE_COUNT = Math.max(250, @@ -931,6 +1045,40 @@ public class JobSchedulerService extends com.android.server.SystemService DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); + + // Set a minimum value on the timeout limit so it's not so low that it interferes with + // legitimate use cases. + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); + final int highestTimeoutCount = Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = Math.max(highestTimeoutCount, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = Math.max(1, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT)); + EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); } private void updateRuntimeConstantsLocked() { @@ -1029,6 +1177,23 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println(); + pw.print(KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, ENABLE_EXECUTION_SAFEGUARDS_UDC) + .println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + EXECUTION_SAFEGUARDS_UDC_ANR_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, + EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS).println(); + pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) @@ -2252,12 +2417,52 @@ public class JobSchedulerService extends com.android.server.SystemService // Set up the app standby bucketing tracker mStandbyTracker = new StandbyTracker(); mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER); - mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, - mConstants.API_QUOTA_SCHEDULE_COUNT, - mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); + + final Categorizer quotaCategorizer = (userId, packageName, tag) -> { + if (QUOTA_TRACKER_TIMEOUT_UIJ_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_EJ_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_REG_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_REG + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_TOTAL_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_ANR_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_ANR + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { + return mConstants.ENABLE_API_QUOTAS + ? QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_SCHEDULE_LOGGED.equals(tag)) { + return mConstants.ENABLE_API_QUOTAS + ? QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + Slog.wtf(TAG, "Unexpected category tag: " + tag); + return QUOTA_TRACKER_CATEGORY_DISABLED; + }; + mQuotaTracker = new CountQuotaTracker(context, quotaCategorizer); + updateQuotaTracker(); // Log at most once per minute. + // Set outside updateQuotaTracker() since this is intentionally not configurable. mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_DISABLED, Integer.MAX_VALUE, 60_000); mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); mAppStandbyInternal.addListener(mStandbyTracker); @@ -2762,6 +2967,48 @@ public class JobSchedulerService extends com.android.server.SystemService 0 /* Reset cumulativeExecutionTime because of successful execution */); } + @VisibleForTesting + void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) { + boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT; + // If madeActive = 0, the job never actually started. + if (!jobTimedOut && jobStatus.madeActive > 0) { + final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive; + // The debug reason may be different if we stopped the job for some other reason + // (eg. constraints), so look at total execution time to be safe. + if (jobStatus.startedAsUserInitiatedJob) { + // TODO: factor in different min guarantees for different UI job types + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + } else if (jobStatus.startedAsExpeditedJob) { + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + } else { + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_GUARANTEE_MS; + } + } + if (jobTimedOut) { + final int userId = jobStatus.getTimeoutBlameUserId(); + final String pkg = jobStatus.getTimeoutBlamePackageName(); + mQuotaTracker.noteEvent(userId, pkg, + jobStatus.startedAsUserInitiatedJob + ? QUOTA_TRACKER_TIMEOUT_UIJ_TAG + : (jobStatus.startedAsExpeditedJob + ? QUOTA_TRACKER_TIMEOUT_EJ_TAG + : QUOTA_TRACKER_TIMEOUT_REG_TAG)); + if (!mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_TIMEOUT_TOTAL_TAG)) { + mAppStandbyInternal.restrictApp( + pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); + } + } + + if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_ANR) { + final int callingUserId = jobStatus.getUserId(); + final String callingPkg = jobStatus.getServiceComponent().getPackageName(); + if (!mQuotaTracker.noteEvent(callingUserId, callingPkg, QUOTA_TRACKER_ANR_TAG)) { + mAppStandbyInternal.restrictApp(callingPkg, callingUserId, + UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); + } + } + } + // JobCompletedListener implementations. /** @@ -2784,6 +3031,8 @@ public class JobSchedulerService extends com.android.server.SystemService mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; + maybeProcessBuggyJob(jobStatus, debugStopReason); + if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) { // The job should have already been cleared from the rest of the JS tracking. No need @@ -3511,26 +3760,36 @@ public class JobSchedulerService extends com.android.server.SystemService if (job.shouldTreatAsUserInitiatedJob() && checkRunUserInitiatedJobsPermission( job.getSourceUid(), job.getSourcePackageName())) { + // The calling package is the one doing the work, so use it in the + // timeout quota checks. + final boolean isWithinTimeoutQuota = mQuotaTracker.isWithinQuota( + job.getTimeoutBlameUserId(), job.getTimeoutBlamePackageName(), + QUOTA_TRACKER_TIMEOUT_UIJ_TAG); + final long upperLimitMs = isWithinTimeoutQuota + ? mConstants.RUNTIME_UI_LIMIT_MS + : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; if (job.getJob().getRequiredNetwork() != null) { // User-initiated data transfers. if (mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS) { final long estimatedTransferTimeMs = mConnectivityController.getEstimatedTransferTimeMs(job); if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) { - return mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; + return Math.min(upperLimitMs, + mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS); } // Try to give the job at least as much time as we think the transfer // will take, but cap it at the maximum limit. final long factoredTransferTimeMs = (long) (estimatedTransferTimeMs * mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR); - return Math.min(mConstants.RUNTIME_UI_LIMIT_MS, + return Math.min(upperLimitMs, Math.max(factoredTransferTimeMs, mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); } - return Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, - mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS); + return Math.min(upperLimitMs, + Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); } - return mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + return Math.min(upperLimitMs, mConstants.RUNTIME_MIN_UI_GUARANTEE_MS); } else if (job.shouldTreatAsExpeditedJob()) { // Don't guarantee RESTRICTED jobs more than 5 minutes. return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX @@ -3547,13 +3806,24 @@ public class JobSchedulerService extends com.android.server.SystemService synchronized (mLock) { if (job.shouldTreatAsUserInitiatedJob() && checkRunUserInitiatedJobsPermission( - job.getSourceUid(), job.getSourcePackageName())) { + job.getSourceUid(), job.getSourcePackageName()) + && mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), + job.getTimeoutBlamePackageName(), + QUOTA_TRACKER_TIMEOUT_UIJ_TAG)) { return mConstants.RUNTIME_UI_LIMIT_MS; } if (job.shouldTreatAsUserInitiatedJob()) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } - return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + // Only let the app use the higher runtime if it hasn't repeatedly timed out. + final String timeoutTag = job.shouldTreatAsExpeditedJob() + ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG; + final long upperLimitMs = + mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), + job.getTimeoutBlamePackageName(), timeoutTag) + ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS + : mConstants.RUNTIME_MIN_GUARANTEE_MS; + return Math.min(upperLimitMs, mConstants.USE_TARE_POLICY ? mTareController.getMaxJobExecutionTimeMsLocked(job) : mQuotaController.getMaxJobExecutionTimeMsLocked(job)); @@ -3797,6 +4067,17 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName, + int timeoutBlameUserId, @NonNull String timeoutBlamePackageName) { + return !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, + QUOTA_TRACKER_ANR_TAG) + || !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, + QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG) + || !mQuotaTracker.isWithinQuota(timeoutBlameUserId, timeoutBlamePackageName, + QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); + } + + @Override public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId, int userId, @NonNull String packageName) { if (packageName == null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 0b08b6faf971..109686d76b2f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()) .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; + boolean startedWithForegroundFlag = false; try { final Context.BindServiceFlags bindFlags; - if (job.shouldTreatAsUserInitiatedJob()) { + if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { + // If the user has bg restricted the app, don't give the job FG privileges + // such as bypassing data saver or getting the higher foreground proc state. + // If we've gotten to this point, the app is most likely in the foreground, + // so the job will run just fine while the user keeps the app in the foreground. bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); - } else if (job.shouldTreatAsExpeditedJob()) { + startedWithForegroundFlag = true; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND @@ -535,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; + // Wait until after bindService() returns a success value to set these so we don't + // have JobStatus objects that aren't running but have these set to true. job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob(); + job.startedWithForegroundFlag = startedWithForegroundFlag; return true; } } @@ -1331,7 +1340,7 @@ public final class JobServiceContext implements ServiceConnection { // FINISHED/NO-RETRY. onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_onStartJob", + "job_scheduler.value_cntr_w_uid_slow_app_response_on_start_job", /* debugReason */ "timed out while starting", /* anrMessage */ "No response to onStartJob", CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, @@ -1343,7 +1352,7 @@ public final class JobServiceContext implements ServiceConnection { // other reason. onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ false, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_onStopJob", + "job_scheduler.value_cntr_w_uid_slow_app_response_on_stop_job", /* debugReason */ "timed out while stopping", /* anrMessage */ "No response to onStopJob", CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, @@ -1400,7 +1409,7 @@ public final class JobServiceContext implements ServiceConnection { } else if (mAwaitingNotification) { onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_setNotification", + "job_scheduler.value_cntr_w_uid_slow_app_response_set_notification", /* debugReason */ "timed out while stopping", /* anrMessage */ "required notification not provided", /* triggerAnr */ true); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index ecee10a13c1d..25b3421a55f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.os.SystemClock; import android.os.UserHandle; @@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); - final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, - jobStatus.canRunInBatterySaver()); + final boolean isUserBgRestricted = + !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() + && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); + // If a job started with the foreground flag, it'll cause the UID to stay active + // and thus cause areJobsRestricted() to always return false, so if + // areJobsRestricted() returns false and the app is BG restricted and not TOP, + // we need to stop any jobs that started with the foreground flag so they don't + // keep the app in an elevated proc state. If we were to get in this situation, + // then the user restricted the app after the job started, so it's best to stop + // the job as soon as possible, especially since the job would be visible to the + // user (with a notification and in Task Manager). + // There are several other reasons that uidActive can be true for an app even if its + // proc state is less important than BFGS. + // JobScheduler has historically (at least up through UDC) allowed the app's jobs to run + // when its UID was active, even if it's background restricted. This has been fine because + // JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves + // will not keep the UID active. The logic here is to ensure that special jobs + // (e.g. user-initiated jobs) themselves do not keep the UID active when the app is + // background restricted. + final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag + && isUserBgRestricted + && mService.getUidProcState(uid) + > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + final boolean canRun = !shouldStopImmediately + && !mAppStateTracker.areJobsRestricted( + uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { @@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController { } boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun, - !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() - && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)); + isUserBgRestricted); didChange |= jobStatus.setUidActive(isActive); return didChange; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index f6bdb9303a04..6d938debde10 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); + if (mBackgroundMeteredAllowed.size() > 0) { + pw.print("Background metered allowed: "); + pw.println(mBackgroundMeteredAllowed); + pw.println(); + } + pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { 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 edd531d13965..13903acc0439 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 @@ -430,6 +430,13 @@ public final class JobStatus { * when it started running. This isn't copied over when a job is rescheduled. */ public boolean startedAsUserInitiatedJob = false; + /** + * Whether this particular JobStatus instance started with the foreground flag + * (or more accurately, did <b>not</b> have the + * {@link android.content.Context#BIND_NOT_FOREGROUND} flag + * included in its binding flags when started). + */ + public boolean startedWithForegroundFlag = false; public boolean startedWithImmediacyPrivilege = false; @@ -1088,13 +1095,77 @@ public final class JobStatus { return UserHandle.getUserId(callingUid); } + private boolean shouldBlameSourceForTimeout() { + // If the system scheduled the job on behalf of an app, assume the app is the one + // doing the work and blame the app directly. This is the case with things like + // syncs via SyncManager. + // If the system didn't schedule the job on behalf of an app, then + // blame the app doing the actual work. Proxied jobs are a little tricky. + // Proxied jobs scheduled by built-in system apps like DownloadManager may be fine + // and we could consider exempting those jobs. For example, in DownloadManager's + // case, all it does is download files and the code is vetted. A timeout likely + // means it's downloading a large file, which isn't an error. For now, DownloadManager + // is an exempted app, so this shouldn't be an issue. + // However, proxied jobs coming from other system apps (such as those that can + // be updated separately from an OTA) may not be fine and we would want to apply + // this policy to those jobs/apps. + // TODO(284512488): consider exempting DownloadManager or other system apps + return UserHandle.isCore(callingUid); + } + + /** + * Returns the package name that should most likely be blamed for the job timing out. + */ + public String getTimeoutBlamePackageName() { + if (shouldBlameSourceForTimeout()) { + return sourcePackageName; + } + return getServiceComponent().getPackageName(); + } + + /** + * Returns the UID that should most likely be blamed for the job timing out. + */ + public int getTimeoutBlameUid() { + if (shouldBlameSourceForTimeout()) { + return sourceUid; + } + return callingUid; + } + + /** + * Returns the userId that should most likely be blamed for the job timing out. + */ + public int getTimeoutBlameUserId() { + if (shouldBlameSourceForTimeout()) { + return sourceUserId; + } + return UserHandle.getUserId(callingUid); + } + /** * Returns an appropriate standby bucket for the job, taking into account any standby * exemptions. */ public int getEffectiveStandbyBucket() { + final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); + final boolean isBuggy = jsi.isAppConsideredBuggy( + getUserId(), getServiceComponent().getPackageName(), + getTimeoutBlameUserId(), getTimeoutBlamePackageName()); + final int actualBucket = getStandbyBucket(); if (actualBucket == EXEMPTED_INDEX) { + // EXEMPTED apps always have their jobs exempted, even if they're buggy, because the + // user has explicitly told the system to avoid restricting the app for power reasons. + if (isBuggy) { + final String pkg; + if (getServiceComponent().getPackageName().equals(sourcePackageName)) { + pkg = sourcePackageName; + } else { + pkg = getServiceComponent().getPackageName() + "/" + sourcePackageName; + } + Slog.w(TAG, "Exempted app " + pkg + " considered buggy"); + } return actualBucket; } if (uidActive || getJob().isExemptedFromAppStandby()) { @@ -1102,13 +1173,18 @@ public final class JobStatus { // like other ACTIVE apps. return ACTIVE_INDEX; } + // 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. + final int highestBucket = isBuggy ? WORKING_INDEX : ACTIVE_INDEX; if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX && mHasMediaBackupExemption) { - // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the + // Treat it as if it's at least WORKING_INDEX since media backup jobs are important + // to the user, and the // source package may not have been used directly in a while. - return Math.min(WORKING_INDEX, actualBucket); + return Math.max(highestBucket, Math.min(WORKING_INDEX, actualBucket)); } - return actualBucket; + return Math.max(highestBucket, actualBucket); } /** Returns the real standby bucket of the job. */ @@ -1537,6 +1613,10 @@ public final class JobStatus { * for any reason. */ public boolean shouldTreatAsUserInitiatedJob() { + // isUserBgRestricted is intentionally excluded from this method. It should be fine to + // treat the job as a UI job while the app is TOP, but just not in the background. + // Instead of adding a proc state check here, the parts of JS that can make the distinction + // and care about the distinction can do the check. return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; @@ -1584,6 +1664,11 @@ public final class JobStatus { && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); } + /** Returns whether or not the app is background restricted by the user (FAS). */ + public boolean isUserBgRestricted() { + return mIsUserBgRestricted; + } + /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); @@ -2733,6 +2818,12 @@ public final class JobStatus { } pw.decreaseIndent(); + pw.print("Started with foreground flag: "); + pw.println(startedWithForegroundFlag); + if (mIsUserBgRestricted) { + pw.println("User BG restricted"); + } + if (changedAuthorities != null) { pw.println("Changed authorities:"); pw.increaseIndent(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 07958dd0fef5..1c29982dbd48 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -773,6 +773,14 @@ public final class QuotaController extends StateController { // If quota is currently "free", then the job can run for the full amount of time, // regardless of bucket (hence using charging instead of isQuotaFreeLocked()). if (mService.isBatteryCharging() + // The top and foreground cases here were added because apps in those states + // aren't really restricted and the work could be something the user is + // waiting for. Now that user-initiated jobs are a defined concept, we may + // not need these exemptions as much. However, UIJs are currently limited + // (as of UDC) to data transfer work. There may be other work that could + // rely on this exception. Once we add more UIJ types, we can re-evaluate + // the need for these exceptions. + // TODO: re-evaluate the need for these exceptions || mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 55e681521048..7d3837786be9 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -3025,7 +3025,7 @@ public class AppStandbyController public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE; public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = - COMPRESS_TIME ? ONE_MINUTE : ONE_DAY; + COMPRESS_TIME ? ONE_MINUTE : ONE_HOUR; public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS = 2 * ONE_MINUTE; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 0ec3847d29f4..46260ea5e658 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -607,6 +607,7 @@ interface IActivityManager { void killPackageDependents(in String packageName, int userId); void makePackageIdle(String packageName, int userId); + void setDeterministicUidIdle(boolean deterministic); int getMemoryTrimLevel(); boolean isVrModePackageEnabled(in ComponentName packageName); void notifyLockedProfile(int userId); diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 57935e3bd5b1..70e924a2acfe 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -2933,62 +2933,21 @@ public class WallpaperManager { } } - if (!isComponentExist(context, cn)) { - cn = null; - } - - return cn; - } - - /** - * Return {@link ComponentName} of the CMF default wallpaper, or - * {@link #getDefaultWallpaperComponent(Context)} if none is defined. - * - * @hide - */ - public static ComponentName getCmfDefaultWallpaperComponent(Context context) { - ComponentName cn = null; - String[] cmfWallpaperMap = context.getResources().getStringArray( - com.android.internal.R.array.cmf_default_wallpaper_component); - if (cmfWallpaperMap == null || cmfWallpaperMap.length == 0) { - Log.d(TAG, "No CMF wallpaper config"); - return getDefaultWallpaperComponent(context); - } - - for (String entry : cmfWallpaperMap) { - String[] cmfWallpaper; - if (!TextUtils.isEmpty(entry)) { - cmfWallpaper = entry.split(","); - if (cmfWallpaper != null && cmfWallpaper.length == 2 && VALUE_CMF_COLOR.equals( - cmfWallpaper[0]) && !TextUtils.isEmpty(cmfWallpaper[1])) { - cn = ComponentName.unflattenFromString(cmfWallpaper[1]); - break; - } + // Check if the package exists + if (cn != null) { + try { + final PackageManager packageManager = context.getPackageManager(); + packageManager.getPackageInfo(cn.getPackageName(), + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } catch (PackageManager.NameNotFoundException e) { + cn = null; } } - if (!isComponentExist(context, cn)) { - cn = null; - } - return cn; } - private static boolean isComponentExist(Context context, ComponentName cn) { - if (cn == null) { - return false; - } - try { - final PackageManager packageManager = context.getPackageManager(); - packageManager.getPackageInfo(cn.getPackageName(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - return true; - } - /** * Register a callback for lock wallpaper observation. Only the OS may use this. * diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index b2a923043dbe..da5e40aedbd2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11369,7 +11369,8 @@ public class DevicePolicyManager { * @throws SecurityException if the caller is not a profile owner on an organization-owned * managed profile. * @throws IllegalStateException if called after the device setup has been completed. - * @throws UnsupportedOperationException if the api is not enabled. + * @throws UnsupportedOperationException if managed subscriptions policy is not explicitly + * enabled by the device policy management role holder during device setup. * @see ManagedSubscriptionsPolicy */ public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) { diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 7be00a045403..3487e0b1f3e8 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -827,6 +827,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS = 1 << 4; + /** + * Whether AbiOverride was used when installing this application. + * @hide + */ + public static final int PRIVATE_FLAG_EXT_CPU_OVERRIDE = 1 << 5; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = { PRIVATE_FLAG_EXT_PROFILEABLE, @@ -834,6 +840,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE, PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK, PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS, + PRIVATE_FLAG_EXT_CPU_OVERRIDE, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlagsExt {} diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 931adb55a686..ef274a56e1d3 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.Activity; +import android.app.ActivityOptions; import android.app.Application.ActivityLifecycleCallbacks; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -535,7 +536,11 @@ public final class PrintManager { return null; } try { - mContext.startIntentSender(intent, null, 0, 0, 0); + ActivityOptions activityOptions = ActivityOptions.makeBasic() + .setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + mContext.startIntentSender(intent, null, 0, 0, 0, + activityOptions.toBundle()); return new PrintJob(printJob, this); } catch (SendIntentException sie) { Log.e(LOG_TAG, "Couldn't start print job config activity.", sie); diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING new file mode 100644 index 000000000000..59b2bc1e4f73 --- /dev/null +++ b/core/java/android/service/notification/TEST_MAPPING @@ -0,0 +1,49 @@ +{ + "presubmit": [ + { + "name": "CtsNotificationTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + }, + { + "name": "FrameworksUiServicesTests", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsNotificationTestCases" + } + ] +} diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7e4e4022f00f..5019b85ca503 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1099,21 +1099,25 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // TODO: Support a ResultReceiver for IME. // TODO(b/123718661): Make show() work for multi-session IME. int typesReady = 0; + final boolean imeVisible = mState.isSourceOrDefaultVisible( + mImeSourceConsumer.getId(), ime()); for (int type = FIRST; type <= LAST; type = type << 1) { if ((types & type) == 0) { continue; } @AnimationType final int animationType = getAnimationType(type); final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0; - final boolean isImeAnimation = type == ime(); - if (requestedVisible && animationType == ANIMATION_TYPE_NONE - || animationType == ANIMATION_TYPE_SHOW) { + final boolean isIme = type == ime(); + var alreadyVisible = requestedVisible && (!isIme || imeVisible) + && animationType == ANIMATION_TYPE_NONE; + var alreadyAnimatingShow = animationType == ANIMATION_TYPE_SHOW; + if (alreadyVisible || alreadyAnimatingShow) { // no-op: already shown or animating in (because window visibility is // applied before starting animation). if (DEBUG) Log.d(TAG, String.format( "show ignored for type: %d animType: %d requestedVisible: %s", type, animationType, requestedVisible)); - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } @@ -1121,13 +1125,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (fromIme && animationType == ANIMATION_TYPE_USER) { // App is already controlling the IME, don't cancel it. - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onFailed( statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } continue; } - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onProgress( statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1bbb7b4bd671..a28be4be24ad 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5459,7 +5459,7 @@ public final class ViewRootImpl implements ViewParent, } private void updateRenderHdrSdrRatio() { - mRenderHdrSdrRatio = mDisplay.getHdrSdrRatio(); + mRenderHdrSdrRatio = Math.min(mDesiredHdrSdrRatio, mDisplay.getHdrSdrRatio()); mUpdateHdrSdrRatioInfo = true; } @@ -5487,22 +5487,14 @@ public final class ViewRootImpl implements ViewParent, mHdrSdrRatioChangedListener = null; } else { mHdrSdrRatioChangedListener = display -> { - setTargetHdrSdrRatio(display.getHdrSdrRatio()); + updateRenderHdrSdrRatio(); + invalidate(); }; mDisplay.registerHdrSdrRatioChangedListener(mExecutor, mHdrSdrRatioChangedListener); } } } - /** happylint */ - public void setTargetHdrSdrRatio(float ratio) { - if (mRenderHdrSdrRatio != ratio) { - mRenderHdrSdrRatio = ratio; - mUpdateHdrSdrRatioInfo = true; - invalidate(); - } - } - @Override public void requestChildFocus(View child, View focused) { if (DEBUG_INPUT_RESIZE) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ea750765799f..739c1bfccd3b 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2065,7 +2065,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get enabled autofill services status."); + throw new RuntimeException("Fail to get enabled autofill services status. " + e); } } @@ -2084,7 +2084,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill services component name."); + throw new RuntimeException("Fail to get autofill services component name. " + e); } } @@ -2111,7 +2111,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data id for field classification."); + throw new RuntimeException("Fail to get user data id for field classification. " + e); } } @@ -2134,7 +2134,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data for field classification."); + throw new RuntimeException("Fail to get user data for field classification. " + e); } } @@ -2174,7 +2174,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get field classification enabled status."); + throw new RuntimeException("Fail to get field classification enabled status. " + e); } } @@ -2198,7 +2198,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get default field classification algorithm."); + throw new RuntimeException("Fail to get default field classification algorithm. " + e); } } @@ -2220,7 +2220,8 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get available field classification algorithms."); + throw new + RuntimeException("Fail to get available field classification algorithms. " + e); } } @@ -2244,7 +2245,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill supported status."); + throw new RuntimeException("Fail to get autofill supported status. " + e); } } diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java index 1d5c6f1f12b1..6f229f6e0054 100644 --- a/core/java/android/webkit/SslErrorHandler.java +++ b/core/java/android/webkit/SslErrorHandler.java @@ -20,11 +20,15 @@ import android.annotation.SystemApi; import android.os.Handler; /** - * Represents a request for handling an SSL error. Instances of this class are - * created by the WebView and passed to - * {@link WebViewClient#onReceivedSslError}. The host application must call - * either {@link #proceed} or {@link #cancel} to set the WebView's response - * to the request. + * Represents a request for handling an SSL error. + * + * <p>A {@link WebView} creates an instance of this class. The instance is + * passed to {@link WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError)}. + * + * <p>The host application must call {@link #cancel()} or, contrary to secure + * web communication standards, {@link #proceed()} to provide the web view's + * response to the request. */ public class SslErrorHandler extends Handler { @@ -35,17 +39,29 @@ public class SslErrorHandler extends Handler { public SslErrorHandler() {} /** - * Proceed with the SSL certificate. - * <p> - * It is not recommended to proceed past SSL errors and this method should - * generally not be used; see {@link WebViewClient#onReceivedSslError} for - * more information. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to ignore the error and continue communicating with the server. + * + * <p class="warning"><b>Warning:</b> When an SSL error occurs, the host + * application should always call {@link #cancel()} rather than + * {@code proceed()} because an invalid SSL certificate means the connection + * is not secure. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void proceed() {} /** - * Cancel this request and all pending requests for the WebView that had - * the error. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to terminate communication with the server. Cancels the current server + * request and all pending requests for the {@code WebView}. + * + * <p>The host application must call this method to prevent a resource from + * loading when an SSL certificate is invalid. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void cancel() {} } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 55f09f110f88..2dfeae3567e5 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -382,30 +382,34 @@ public class WebViewClient { } /** - * Notify the host application that an SSL error occurred while loading a - * resource. The host application must call either {@link SslErrorHandler#cancel} or - * {@link SslErrorHandler#proceed}. Note that the decision may be retained for use in - * response to future SSL errors. The default behavior is to cancel the - * load. - * <p> - * This API is only called for recoverable SSL certificate errors. In the case of - * non-recoverable errors (such as when the server fails the client), WebView will call {@link - * #onReceivedError(WebView, WebResourceRequest, WebResourceError)} with {@link - * #ERROR_FAILED_SSL_HANDSHAKE}. - * <p> - * Applications are advised not to prompt the user about SSL errors, as - * the user is unlikely to be able to make an informed security decision - * and WebView does not provide any UI for showing the details of the + * Notifies the host application that an SSL error occurred while loading a + * resource. The host application must call either + * {@link SslErrorHandler#cancel()} or {@link SslErrorHandler#proceed()}. + * + * <p class="warning"><b>Warning:</b> Application overrides of this method + * can be used to display custom error pages or to silently log issues, but + * the host application should always call {@code SslErrorHandler#cancel()} + * and never proceed past errors. + * + * <p class="note"><b>Note:</b> Do not prompt the user about SSL errors. + * Users are unlikely to be able to make an informed security decision, and + * {@code WebView} does not provide a UI for showing the details of the * error in a meaningful way. - * <p> - * Application overrides of this method may display custom error pages or - * silently log issues, but it is strongly recommended to always call - * {@link SslErrorHandler#cancel} and never allow proceeding past errors. * - * @param view The WebView that is initiating the callback. - * @param handler An {@link SslErrorHandler} that will handle the user's - * response. - * @param error The SSL error object. + * <p>The decision to call {@code proceed()} or {@code cancel()} may be + * retained to facilitate responses to future SSL errors. The default + * behavior is to cancel the resource loading process. + * + * <p>This API is called only for recoverable SSL certificate errors. For + * non-recoverable errors (such as when the server fails the client), the + * {@code WebView} calls {@link #onReceivedError(WebView, + * WebResourceRequest, WebResourceError) onReceivedError(WebView, + * WebResourceRequest, WebResourceError)} with the + * {@link #ERROR_FAILED_SSL_HANDSHAKE} argument. + * + * @param view {@code WebView} that initiated the callback. + * @param handler {@link SslErrorHandler} that handles the user's response. + * @param error SSL error object. */ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 59238b40e0c8..d2a16a3a9212 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -550,6 +550,16 @@ public final class TransitionInfo implements Parcelable { } /** + * Updates the callsites of all the surfaces in this transition, which aids in the debugging of + * lingering surfaces. + */ + public void setUnreleasedWarningCallSiteForAllSurfaces(String callsite) { + for (int i = mChanges.size() - 1; i >= 0; --i) { + mChanges.get(i).getLeash().setUnreleasedWarningCallSite(callsite); + } + } + + /** * Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol * refcounts are incremented which allows the "remote" receiver to release them without breaking * the caller's references. Use this only if you need to "send" this to a local function which diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index bf265689c0e0..4e7bfe50cd30 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -16,42 +16,50 @@ package com.android.internal.app; -import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; +import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.CombinedVibration; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.os.VibrationEffect; +import android.os.VibratorManager; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.widget.AnalogClock; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.R; import org.json.JSONObject; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.util.Random; /** * @hide @@ -59,30 +67,160 @@ import java.time.ZonedDateTime; public class PlatLogoActivity extends Activity { private static final String TAG = "PlatLogoActivity"; - private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s"; + private static final long LAUNCH_TIME = 5000L; + + private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u"; + + private static final float MIN_WARP = 1f; + private static final float MAX_WARP = 10f; // after all these years + private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false; - private SettableAnalogClock mClock; private ImageView mLogo; - private BubblesDrawable mBg; + private Starfield mStarfield; + + private FrameLayout mLayout; + + private TimeAnimator mAnim; + private ObjectAnimator mWarpAnim; + private Random mRandom; + private float mDp; + + private RumblePack mRumble; + + private boolean mAnimationsEnabled = true; + + private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + measureTouchPressure(event); + startWarp(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + stopWarp(); + break; + } + return true; + } + + }; + + private final Runnable mLaunchNextStage = () -> { + stopWarp(); + launchNextStage(false); + }; + + private final TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + mStarfield.update(deltaTime); + final float warpFrac = (mStarfield.getWarp() - MIN_WARP) / (MAX_WARP - MIN_WARP); + if (mAnimationsEnabled) { + mLogo.setTranslationX(mRandom.nextFloat() * warpFrac * 5 * mDp); + mLogo.setTranslationY(mRandom.nextFloat() * warpFrac * 5 * mDp); + } + if (warpFrac > 0f) { + mRumble.rumble(warpFrac); + } + mLayout.postInvalidate(); + } + }; + + private class RumblePack implements Handler.Callback { + private static final int MSG = 6464; + private static final int INTERVAL = 50; + + private final VibratorManager mVibeMan; + private final HandlerThread mVibeThread; + private final Handler mVibeHandler; + private boolean mSpinPrimitiveSupported; + + private long mLastVibe = 0; + + @SuppressLint("MissingPermission") + @Override + public boolean handleMessage(Message msg) { + final float warpFrac = msg.arg1 / 100f; + if (mSpinPrimitiveSupported) { + if (msg.getWhen() > mLastVibe + INTERVAL) { + mLastVibe = msg.getWhen(); + mVibeMan.vibrate(CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(PRIMITIVE_SPIN, (float) Math.pow(warpFrac, 3.0)) + .compose() + )); + } + } else { + if (mRandom.nextFloat() < warpFrac) { + mLogo.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + } + } + return false; + } + RumblePack() { + mVibeMan = getSystemService(VibratorManager.class); + mSpinPrimitiveSupported = mVibeMan.getDefaultVibrator() + .areAllPrimitivesSupported(PRIMITIVE_SPIN); + + mVibeThread = new HandlerThread("VibratorThread"); + mVibeThread.start(); + mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this); + } + + public void destroy() { + mVibeThread.quit(); + } + + private void rumble(float warpFrac) { + if (!mVibeThread.isAlive()) return; + + final Message msg = Message.obtain(); + msg.what = MSG; + msg.arg1 = (int) (warpFrac * 100); + mVibeHandler.removeMessages(MSG); + mVibeHandler.sendMessage(msg); + } + + } @Override - protected void onPause() { - super.onPause(); + protected void onDestroy() { + mRumble.destroy(); + + super.onDestroy(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setDecorFitsSystemWindows(false); getWindow().setNavigationBarColor(0); getWindow().setStatusBarColor(0); + getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); final ActionBar ab = getActionBar(); if (ab != null) ab.hide(); - final FrameLayout layout = new FrameLayout(this); + try { + mAnimationsEnabled = Settings.Global.getFloat(getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE) > 0f; + } catch (Settings.SettingNotFoundException e) { + mAnimationsEnabled = true; + } - mClock = new SettableAnalogClock(this); + mRumble = new RumblePack(); + + mLayout = new FrameLayout(this); + mRandom = new Random(); + mDp = getResources().getDisplayMetrics().density; + mStarfield = new Starfield(mRandom, mDp * 2f); + mStarfield.setVelocity( + 200f * (mRandom.nextFloat() - 0.5f), + 200f * (mRandom.nextFloat() - 0.5f)); + mLayout.setBackground(mStarfield); final DisplayMetrics dm = getResources().getDisplayMetrics(); final float dp = dm.density; @@ -90,22 +228,79 @@ public class PlatLogoActivity extends Activity { final int widgetSize = (int) (minSide * 0.75); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize); lp.gravity = Gravity.CENTER; - layout.addView(mClock, lp); mLogo = new ImageView(this); - mLogo.setVisibility(View.GONE); mLogo.setImageResource(R.drawable.platlogo); - layout.addView(mLogo, lp); + mLogo.setOnTouchListener(mTouchListener); + mLogo.requestFocus(); + mLayout.addView(mLogo, lp); + + Log.v(TAG, "Hello"); + + setContentView(mLayout); + } + + private void startAnimating() { + mAnim = new TimeAnimator(); + mAnim.setTimeListener(mTimeListener); + mAnim.start(); + } + + private void stopAnimating() { + mAnim.cancel(); + mAnim = null; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + if (event.getRepeatCount() == 0) { + startWarp(); + } + return true; + } + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + stopWarp(); + return true; + } + return false; + } - mBg = new BubblesDrawable(); - mBg.setLevel(0); - mBg.avoid = widgetSize / 2; - mBg.padding = 0.5f * dp; - mBg.minR = 1 * dp; - layout.setBackground(mBg); - layout.setOnLongClickListener(mBg); + private void startWarp() { + stopWarp(); + mWarpAnim = ObjectAnimator.ofFloat(mStarfield, "warp", MIN_WARP, MAX_WARP) + .setDuration(LAUNCH_TIME); + mWarpAnim.start(); + + mLogo.postDelayed(mLaunchNextStage, LAUNCH_TIME + 1000L); + } - setContentView(layout); + private void stopWarp() { + if (mWarpAnim != null) { + mWarpAnim.cancel(); + mWarpAnim.removeAllListeners(); + mWarpAnim = null; + } + mStarfield.setWarp(1f); + mLogo.removeCallbacks(mLaunchNextStage); + } + + @Override + public void onResume() { + super.onResume(); + startAnimating(); + } + + @Override + public void onPause() { + stopWarp(); + stopAnimating(); + super.onPause(); } private boolean shouldWriteSettings() { @@ -113,38 +308,14 @@ public class PlatLogoActivity extends Activity { } private void launchNextStage(boolean locked) { - mClock.animate() - .alpha(0f).scaleX(0.5f).scaleY(0.5f) - .withEndAction(() -> mClock.setVisibility(View.GONE)) - .start(); - - mLogo.setAlpha(0f); - mLogo.setScaleX(0.5f); - mLogo.setScaleY(0.5f); - mLogo.setVisibility(View.VISIBLE); - mLogo.animate() - .alpha(1f) - .scaleX(1f) - .scaleY(1f) - .setInterpolator(new OvershootInterpolator()) - .start(); - - mLogo.postDelayed(() -> { - final ObjectAnimator anim = ObjectAnimator.ofInt(mBg, "level", 0, 10000); - anim.setInterpolator(new DecelerateInterpolator(1f)); - anim.start(); - }, - 500 - ); - final ContentResolver cr = getContentResolver(); try { if (shouldWriteSettings()) { - Log.v(TAG, "Saving egg unlock=" + locked); + Log.v(TAG, "Saving egg locked=" + locked); syncTouchPressure(); Settings.System.putLong(cr, - S_EGG_UNLOCK_SETTING, + U_EGG_UNLOCK_SETTING, locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { @@ -152,14 +323,18 @@ public class PlatLogoActivity extends Activity { } try { - startActivity(new Intent(Intent.ACTION_MAIN) + final Intent eggActivity = new Intent(Intent.ACTION_MAIN) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .addCategory("com.android.internal.category.PLATLOGO")); + .addCategory("com.android.internal.category.PLATLOGO"); + Log.v(TAG, "launching: " + eggActivity); + startActivity(eggActivity); } catch (ActivityNotFoundException ex) { Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } - //finish(); // no longer finish upon unlock; it's fun to frob the dial + if (FINISH_AFTER_NEXT_STAGE_LAUNCH) { + finish(); // we're done here. + } } static final String TOUCH_STATS = "touch.stats"; @@ -217,266 +392,111 @@ public class PlatLogoActivity extends Activity { super.onStop(); } - /** - * Subclass of AnalogClock that allows the user to flip up the glass and adjust the hands. - */ - public class SettableAnalogClock extends AnalogClock { - private int mOverrideHour = -1; - private int mOverrideMinute = -1; - private boolean mOverride = false; + private static class Starfield extends Drawable { + private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE + + private static final int NUM_PLANES = 2; + private final float[] mStars = new float[NUM_STARS * 4]; + private float mVx, mVy; + private long mDt = 0; + private final Paint mStarPaint; - public SettableAnalogClock(Context context) { - super(context); + private final Random mRng; + private final float mSize; + + private final Rect mSpace = new Rect(); + private float mWarp = 1f; + + private float mBuffer; + + public void setWarp(float warp) { + mWarp = warp; } - @Override - protected Instant now() { - final Instant realNow = super.now(); - final ZoneId tz = Clock.systemDefaultZone().getZone(); - final ZonedDateTime zdTime = realNow.atZone(tz); - if (mOverride) { - if (mOverrideHour < 0) { - mOverrideHour = zdTime.getHour(); - } - return Clock.fixed(zdTime - .withHour(mOverrideHour) - .withMinute(mOverrideMinute) - .withSecond(0) - .toInstant(), tz).instant(); - } else { - return realNow; - } + public float getWarp() { + return mWarp; } - double toPositiveDegrees(double rad) { - return (Math.toDegrees(rad) + 360 - 90) % 360; + Starfield(Random rng, float size) { + mRng = rng; + mSize = size; + mStarPaint = new Paint(); + mStarPaint.setStyle(Paint.Style.STROKE); + mStarPaint.setColor(Color.WHITE); } @Override - public boolean onTouchEvent(MotionEvent ev) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mOverride = true; - // pass through - case MotionEvent.ACTION_MOVE: - measureTouchPressure(ev); - - float x = ev.getX(); - float y = ev.getY(); - float cx = getWidth() / 2f; - float cy = getHeight() / 2f; - float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy)); - - int minutes = (75 - (int) (angle / 6)) % 60; - int minuteDelta = minutes - mOverrideMinute; - if (minuteDelta != 0) { - if (Math.abs(minuteDelta) > 45 && mOverrideHour >= 0) { - int hourDelta = (minuteDelta < 0) ? 1 : -1; - mOverrideHour = (mOverrideHour + 24 + hourDelta) % 24; - } - mOverrideMinute = minutes; - if (mOverrideMinute == 0) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - if (getScaleX() == 1f) { - setScaleX(1.05f); - setScaleY(1.05f); - animate().scaleX(1f).scaleY(1f).setDuration(150).start(); - } - } else { - performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); - } - - onTimeChanged(); - postInvalidate(); - } - - return true; - case MotionEvent.ACTION_UP: - if (mOverrideMinute == 0 && (mOverrideHour % 12) == 1) { - Log.v(TAG, "13:00"); - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - launchNextStage(false); - } - return true; + public void onBoundsChange(Rect bounds) { + mSpace.set(bounds); + mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP; + mSpace.inset(-(int) mBuffer, -(int) mBuffer); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + mStars[4 * i] = mRng.nextFloat() * w; + mStars[4 * i + 1] = mRng.nextFloat() * h; + mStars[4 * i + 2] = mStars[4 * i]; + mStars[4 * i + 3] = mStars[4 * i + 1]; } - return false; } - } - private static final String[][] EMOJI_SETS = { - {"🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑", - "🍒", "🍓", "🫐", "🥝"}, - {"😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"}, - {"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "🫠", "😉", "😊", - "😇", "🥰", "😍", "🤩", "😘", "😗", "☺️", "😚", "😙", "🥲", "😋", "😛", "😜", - "🤪", "😝", "🤑", "🤗", "🤭", "🫢", "🫣", "🤫", "🤔", "🫡", "🤐", "🤨", "😐", - "😑", "😶", "🫥", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪", "🤤", "😴", - "😷"}, - { "🤩", "😍", "🥰", "😘", "🥳", "🥲", "🥹" }, - { "🫠" }, - {"💘", "💝", "💖", "💗", "💓", "💞", "💕", "❣", "💔", "❤", "🧡", "💛", - "💚", "💙", "💜", "🤎", "🖤", "🤍"}, - // {"👁", "️🫦", "👁️"}, // this one is too much - {"👽", "🛸", "✨", "🌟", "💫", "🚀", "🪐", "🌙", "⭐", "🌍"}, - {"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"}, - {"🐙", "🪸", "🦑", "🦀", "🦐", "🐡", "🦞", "🐠", "🐟", "🐳", "🐋", "🐬", "🫧", "🌊", - "🦈"}, - {"🙈", "🙉", "🙊", "🐵", "🐒"}, - {"♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"}, - {"🕛", "🕧", "🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡", - "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦"}, - {"🌺", "🌸", "💮", "🏵️", "🌼", "🌿"}, - {"🐢", "✨", "🌟", "👑"} - }; - - static class Bubble { - public float x, y, r; - public int color; - public String text = null; - } - - class BubblesDrawable extends Drawable implements View.OnLongClickListener { - private static final int MAX_BUBBS = 2000; - - private final int[] mColorIds = { - android.R.color.system_accent3_400, - android.R.color.system_accent3_500, - android.R.color.system_accent3_600, - - android.R.color.system_accent2_400, - android.R.color.system_accent2_500, - android.R.color.system_accent2_600, - }; - - private int[] mColors = new int[mColorIds.length]; - - private int mEmojiSet = -1; - - private final Bubble[] mBubbs = new Bubble[MAX_BUBBS]; - private int mNumBubbs; - - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - public float avoid = 0f; - public float padding = 0f; - public float minR = 0f; - - BubblesDrawable() { - for (int i = 0; i < mColorIds.length; i++) { - mColors[i] = getColor(mColorIds[i]); - } - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j] = new Bubble(); - } + public void setVelocity(float x, float y) { + mVx = x; + mVy = y; } @Override - public void draw(Canvas canvas) { - if (getLevel() == 0) return; - final float f = getLevel() / 10000f; - mPaint.setStyle(Paint.Style.FILL); - mPaint.setTextAlign(Paint.Align.CENTER); - int drawn = 0; - for (int j = 0; j < mNumBubbs; j++) { - if (mBubbs[j].color == 0 || mBubbs[j].r == 0) continue; - if (mBubbs[j].text != null) { - mPaint.setTextSize(mBubbs[j].r * 1.75f); - canvas.drawText(mBubbs[j].text, mBubbs[j].x, - mBubbs[j].y + mBubbs[j].r * f * 0.6f, mPaint); - } else { - mPaint.setColor(mBubbs[j].color); - canvas.drawCircle(mBubbs[j].x, mBubbs[j].y, mBubbs[j].r * f, mPaint); + public void draw(@NonNull Canvas canvas) { + final float dtSec = mDt / 1000f; + final float dx = (mVx * dtSec * mWarp); + final float dy = (mVy * dtSec * mWarp); + + final boolean inWarp = mWarp > 1f; + + canvas.drawColor(Color.BLACK); // 0xFF16161D); + + if (mDt > 0 && mDt < 1000) { + canvas.translate( + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f), + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f) + ); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1; + mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w; + mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h; + mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100; + mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100; } - drawn++; } - } - - public void chooseEmojiSet() { - mEmojiSet = (int) (Math.random() * EMOJI_SETS.length); - final String[] emojiSet = EMOJI_SETS[mEmojiSet]; - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j].text = emojiSet[(int) (Math.random() * emojiSet.length)]; + final int slice = (mStars.length / NUM_PLANES / 4) * 4; + for (int p = 0; p < NUM_PLANES; p++) { + mStarPaint.setStrokeWidth(mSize * (p + 1)); + if (inWarp) { + canvas.drawLines(mStars, p * slice, slice, mStarPaint); + } + canvas.drawPoints(mStars, p * slice, slice, mStarPaint); } - invalidateSelf(); } @Override - protected boolean onLevelChange(int level) { - invalidateSelf(); - return true; - } + public void setAlpha(int alpha) { - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - randomize(); - } - - private void randomize() { - final float w = getBounds().width(); - final float h = getBounds().height(); - final float maxR = Math.min(w, h) / 3f; - mNumBubbs = 0; - if (avoid > 0f) { - mBubbs[mNumBubbs].x = w / 2f; - mBubbs[mNumBubbs].y = h / 2f; - mBubbs[mNumBubbs].r = avoid; - mBubbs[mNumBubbs].color = 0; - mNumBubbs++; - } - for (int j = 0; j < MAX_BUBBS; j++) { - // a simple but time-tested bubble-packing algorithm: - // 1. pick a spot - // 2. shrink the bubble until it is no longer overlapping any other bubble - // 3. if the bubble hasn't popped, keep it - int tries = 5; - while (tries-- > 0) { - float x = (float) Math.random() * w; - float y = (float) Math.random() * h; - float r = Math.min(Math.min(x, w - x), Math.min(y, h - y)); - - // shrink radius to fit other bubbs - for (int i = 0; i < mNumBubbs; i++) { - r = (float) Math.min(r, - Math.hypot(x - mBubbs[i].x, y - mBubbs[i].y) - mBubbs[i].r - - padding); - if (r < minR) break; - } - - if (r >= minR) { - // we have found a spot for this bubble to live, let's save it and move on - r = Math.min(maxR, r); - - mBubbs[mNumBubbs].x = x; - mBubbs[mNumBubbs].y = y; - mBubbs[mNumBubbs].r = r; - mBubbs[mNumBubbs].color = mColors[(int) (Math.random() * mColors.length)]; - mNumBubbs++; - break; - } - } - } - Log.v(TAG, String.format("successfully placed %d bubbles (%d%%)", - mNumBubbs, (int) (100f * mNumBubbs / MAX_BUBBS))); } @Override - public void setAlpha(int alpha) { } + public void setColorFilter(@Nullable ColorFilter colorFilter) { - @Override - public void setColorFilter(ColorFilter colorFilter) { } + } @Override public int getOpacity() { - return TRANSLUCENT; + return PixelFormat.OPAQUE; } - @Override - public boolean onLongClick(View v) { - if (getLevel() == 0) return false; - chooseEmojiSet(); - return true; + public void update(long dt) { + mDt = dt; } } - -} +}
\ No newline at end of file diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 8135f9cd2f46..dd52de4f84c7 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -118,6 +118,9 @@ public final class SystemUiDeviceConfigFlags { */ public static final String NAS_DEFAULT_SERVICE = "nas_default_service"; + /** (boolean) Whether notify() calls to NMS should acquire and hold WakeLocks. */ + public static final String NOTIFY_WAKELOCK = "nms_notify_wakelock"; + // Flags related to media notifications /** diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index 3d95dd341cc0..c9e76009136a 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -81,6 +81,10 @@ public class SystemUiSystemPropertiesFlags { /** Gating the logging of DND state change events. */ public static final Flag LOG_DND_STATE_EVENTS = releasedFlag("persist.sysui.notification.log_dnd_state_events"); + + /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ + public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = + devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); } //// == End of flags. Everything below this line is the implementation. == //// diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index da214868ca05..f3acab063bbf 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -13,33 +13,186 @@ Copyright (C) 2021 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<vector android:height="128dp" - android:width="128dp" - android:viewportHeight="24" - android:viewportWidth="24" - xmlns:android="http://schemas.android.com/apk/res/android"> - <path - android:fillColor="@android:color/system_accent1_400" - android:pathData="M11,0.3c0.6,-0.3 1.4,-0.3 2,0l0.6,0.4c0.7,0.4 1.4,0.6 2.2,0.6l0.7,-0.1c0.7,0 1.4,0.3 1.8,0.9l0.3,0.6c0.4,0.7 1,1.2 1.7,1.5L21,4.5c0.7,0.3 1.1,0.9 1.2,1.7v0.7C22.2,7.7 22.5,8.4 23,9l0.4,0.5c0.4,0.6 0.5,1.3 0.2,2l-0.3,0.6c-0.3,0.7 -0.4,1.5 -0.3,2.3l0.1,0.7c0.1,0.7 -0.2,1.4 -0.7,1.9L22,17.5c-0.6,0.5 -1.1,1.1 -1.3,1.9L20.5,20c-0.2,0.7 -0.8,1.2 -1.5,1.4l-0.7,0.1c-0.8,0.2 -1.4,0.5 -2,1.1l-0.5,0.5c-0.5,0.5 -1.3,0.7 -2,0.5l-0.6,-0.2c-0.8,-0.2 -1.5,-0.2 -2.3,0l-0.6,0.2c-0.7,0.2 -1.5,0 -2,-0.5l-0.5,-0.5c-0.5,-0.5 -1.2,-0.9 -2,-1.1L5,21.4c-0.7,-0.2 -1.3,-0.7 -1.5,-1.4l-0.2,-0.7C3.1,18.6 2.6,18 2,17.5l-0.6,-0.4c-0.6,-0.5 -0.8,-1.2 -0.7,-1.9l0.1,-0.7c0.1,-0.8 0,-1.6 -0.3,-2.3l-0.3,-0.6c-0.3,-0.7 -0.2,-1.4 0.2,-2L1,9c0.5,-0.6 0.7,-1.4 0.8,-2.2V6.2C1.9,5.5 2.3,4.8 3,4.5l0.6,-0.3c0.7,-0.3 1.3,-0.9 1.7,-1.5l0.3,-0.6c0.4,-0.6 1.1,-1 1.8,-0.9l0.7,0.1c0.8,0 1.6,-0.2 2.2,-0.6L11,0.3z" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="512dp" + android:height="512dp" + android:viewportWidth="512" + android:viewportHeight="512"> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"> + <aapt:attr name="android:fillColor"> + <gradient + android:startX="256" + android:startY="21.81" + android:endX="256" + android:endY="350.42" + android:type="linear"> + <item android:offset="0" android:color="#FF073042"/> + <item android:offset="1" android:color="#FF073042"/> + </gradient> + </aapt:attr> + </path> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M171.92,216.82h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,275.73h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M369.04,337.63h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M285.93,252.22h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M318.96,218.84h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M294.05,288.55h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M330.82,273.31h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,298.95h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M220.14,238.94h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M272.1,318.9h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M293.34,349.25h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M161.05,254.24h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M378.92,192h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M137.87,323.7h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0" + android:strokeWidth="56.561" + android:fillColor="#00000000" + android:strokeColor="#f86734"/> + <path + android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z" + android:fillColor="#3ddc84"/> + <path + android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z" + android:fillColor="#fff"/> + <path + android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z" + android:fillColor="#fff"/> + <path + android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z" + android:fillColor="#fff"/> + <path + android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z" + android:fillColor="#fff"/> + <path + android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z" + android:fillColor="#fff"/> + <path + android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z" + android:fillColor="#fff"/> + <path + android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z" + android:fillColor="#fff"/> + <path + android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z" + android:fillColor="#fff"/> + <path + android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z" + android:fillColor="#fff"/> </vector> + diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index b58639fba6d9..0197f21e2b57 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2172,7 +2172,7 @@ <string name="miniresolver_sms_information" msgid="4311292661329483088">"Teie organisatsioon lubab sõnumeid saata ainult töörakendustest."</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Kasuta isiklikku brauserit"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Kasuta tööbrauserit"</string> - <string name="miniresolver_call" msgid="6386870060423480765">"Helistage"</string> + <string name="miniresolver_call" msgid="6386870060423480765">"Helista"</string> <string name="miniresolver_switch" msgid="8011924662117617451">"Lülitu"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIM-kaardi võrgu avamise PIN-kood"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIM-kaardi võrgu alamhulga avamise PIN-kood"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 50685cb5994f..84f56ff54f36 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -687,7 +687,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"צריך להזיז את הטלפון שמאלה"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"צריך להזיז את הטלפון ימינה"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"יש להביט ישירות אל המכשיר."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"אי אפשר לראות את הפנים שלך. יש להחזיק את הטלפון בגובה העיניים."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"כדי לראות את הפנים שלך יש להחזיק את הטלפון בגובה העיניים."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"יותר מדי תנועה. יש להחזיק את הטלפון בצורה יציבה."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"יש לסרוק שוב את הפנים."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"לא ניתן לזהות את הפנים. יש לנסות שוב."</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index ed22dfa8521c..93a7352eb7e0 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"색상 보정"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 모드"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"보청기"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"청각 보조 기기"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"볼륨 키에서 손을 뗍니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>을 켜려면 볼륨 키 2개를 3초 동안 길게 누르세요."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index b0762d29b3cb..2ad8a50f412b 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -155,7 +155,7 @@ <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nuk u transferua"</string> <string name="fcComplete" msgid="1080909484660507044">"Kodi i funksionit është i plotë."</string> <string name="fcError" msgid="5325116502080221346">"Problem me lidhjen ose kod është i pavlefshëm."</string> - <string name="httpErrorOk" msgid="6206751415788256357">"Në rregull!"</string> + <string name="httpErrorOk" msgid="6206751415788256357">"Në rregull"</string> <string name="httpError" msgid="3406003584150566720">"Pati një gabim në rrjet."</string> <string name="httpErrorLookup" msgid="3099834738227549349">"Nuk mundi ta gjente URL-në."</string> <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Skema e vërtetimit nuk mbështetet."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 871107ad65a8..815c9a94193e 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"Färgkorrigering"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörapparater"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörhjälpmedel"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Släpp volymknapparna. Du kan aktivera <xliff:g id="SERVICE_NAME">%1$s</xliff:g> genom att hålla båda volymknapparna nedtryckta i tre sekunder igen."</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 5b647fac3ca2..14ced72ebf01 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -461,10 +461,10 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"允許應用程式修改平板電腦的通話記錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改你的通話記錄。"</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"允許應用程式修改 Android TV 裝置的通話記錄,包括來電和撥出電話相關資料。惡意應用程式可能會藉此清除或修改你的通話記錄。"</string> <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"允許應用程式修改手機的通話記錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改你的通話記錄。"</string> - <string name="permlab_bodySensors" msgid="662918578601619569">"在使用期間可存取人體感應器資料,例如心跳速率"</string> - <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"允許應用程式在使用期間存取人體感應器資料,例如心跳速率、體溫和血氧比例。"</string> - <string name="permlab_bodySensors_background" msgid="4912560779957760446">"在背景執行時可存取人體感應器資料,例如心跳速率"</string> - <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"允許應用程式在背景執行時存取人體感應器資料,例如心跳速率、體溫和血氧比例。"</string> + <string name="permlab_bodySensors" msgid="662918578601619569">"在使用期間可存取人體感應器資料,例如心率"</string> + <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"允許應用程式在使用期間存取人體感應器資料,例如心率、體溫和血氧比例。"</string> + <string name="permlab_bodySensors_background" msgid="4912560779957760446">"在背景執行時可存取人體感應器資料,例如心率"</string> + <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"允許應用程式在背景執行時存取人體感應器資料,例如心率、體溫和血氧比例。"</string> <string name="permlab_readCalendar" msgid="6408654259475396200">"讀取日曆活動和詳細資訊"</string> <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"這個應用程式可讀取所有儲存在平板電腦上的日曆活動資訊,以及共用或儲存日曆資料。"</string> <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"這個應用程式可讀取所有儲存在 Android TV 裝置上的日曆活動,以及共用或儲存日曆資料。"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 00f8db0d9264..ee8c0f83304e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1817,16 +1817,6 @@ specified --> <string name="default_wallpaper_component" translatable="false">@null</string> - <!-- CMF colors to default wallpaper component map, the component with color matching the device - color will be the cmf default wallpapers. The default wallpaper will be default wallpaper - component if not specified. - - E.g. for SLV color, and com.android.example/com.android.example.SlVDefaultWallpaper - <item>SLV,com.android.example/com.android.example.SlVDefaultWallpaper</item> --> - <string-array name="cmf_default_wallpaper_component" translatable="false"> - <!-- Add packages here --> - </string-array> - <!-- By default a product has no distinct default lock wallpaper --> <item name="default_lock_wallpaper" type="drawable">@null</item> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index fb3acbe79114..a5b2b853fddd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5035,8 +5035,8 @@ <string name="display_rotation_camera_compat_toast_after_rotation">Rotate for a better view</string> <!-- Text on a toast shown when a camera view is started within the app that may not be able - to display the camera preview correctly while in split screen. [CHAR LIMIT=NONE] --> - <string name="display_rotation_camera_compat_toast_in_split_screen">Exit split screen for a better view</string> + to display the camera preview correctly while in multi-window. [CHAR LIMIT=NONE] --> + <string name="display_rotation_camera_compat_toast_in_multi_window">Open <xliff:g id="name" example="MyApp">%s</xliff:g> in full screen for a better view</string> <!-- Label for button to confirm chosen date or time [CHAR LIMIT=30] --> <string name="done_label">Done</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6ab671a70c3c..dc4eafd2e00e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2116,7 +2116,6 @@ <java-symbol type="string" name="data_usage_rapid_body" /> <java-symbol type="string" name="data_usage_rapid_app_body" /> <java-symbol type="string" name="default_wallpaper_component" /> - <java-symbol type="array" name="cmf_default_wallpaper_component" /> <java-symbol type="string" name="device_storage_monitor_notification_channel" /> <java-symbol type="string" name="dlg_ok" /> <java-symbol type="string" name="dump_heap_notification" /> @@ -2533,7 +2532,7 @@ <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="string" name="zen_mode_default_every_night_name" /> <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> - <java-symbol type="string" name="display_rotation_camera_compat_toast_in_split_screen" /> + <java-symbol type="string" name="display_rotation_camera_compat_toast_in_multi_window" /> <java-symbol type="array" name="config_system_condition_providers" /> <java-symbol type="string" name="muted_by" /> <java-symbol type="string" name="zen_mode_alarm" /> diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..e7b1110f898a 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + var p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + var p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index e4defcfa359f..93e44f1d2f87 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1813,6 +1813,12 @@ "group": "WM_DEBUG_KEEP_SCREEN_ON", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-479665533": { + "message": "DisplayRotationCompatPolicy: Multi-window toast not shown as package '%s' cannot be found.", + "level": "ERROR", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" + }, "-464564167": { "message": "Current transition prevents automatic focus change", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 2307d6080f9f..b9d3756ac6d2 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -997,12 +997,63 @@ public final class Bitmap implements Parcelable { canvas.concat(m); canvas.drawBitmap(source, srcR, dstR, paint); canvas.setBitmap(null); + + // If the source has a gainmap, apply the same set of transformations to the gainmap + // and set it on the output + if (source.hasGainmap()) { + Bitmap newMapContents = transformGainmap(source, m, neww, newh, paint, srcR, dstR, + deviceR); + if (newMapContents != null) { + bitmap.setGainmap(new Gainmap(source.getGainmap(), newMapContents)); + } + } + if (isHardware) { return bitmap.copy(Config.HARDWARE, false); } return bitmap; } + private static Bitmap transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint, + Rect srcR, RectF dstR, RectF deviceR) { + Canvas canvas; + Bitmap sourceGainmap = source.getGainmap().getGainmapContents(); + // Gainmaps can be scaled relative to the base image (eg, 1/4th res) + // Preserve that relative scaling between the base & gainmap in the output + float scaleX = (sourceGainmap.getWidth() / (float) source.getWidth()); + float scaleY = (sourceGainmap.getHeight() / (float) source.getHeight()); + int mapw = Math.round(neww * scaleX); + int maph = Math.round(newh * scaleY); + + if (mapw == 0 || maph == 0) { + // The gainmap has been scaled away entirely, drop it + return null; + } + + // Scale the computed `srcR` used for rendering the source bitmap to the destination + // to be in gainmap dimensions + Rect gSrcR = new Rect((int) (srcR.left * scaleX), + (int) (srcR.top * scaleY), (int) (srcR.right * scaleX), + (int) (srcR.bottom * scaleY)); + + // Note: createBitmap isn't used as that requires a non-null colorspace, however + // gainmaps don't have a colorspace. So use `nativeCreate` directly to bypass + // that colorspace enforcement requirement (#getColorSpace() allows a null return) + Bitmap newMapContents = nativeCreate(null, 0, mapw, mapw, maph, + sourceGainmap.getConfig().nativeInt, true, 0); + newMapContents.eraseColor(0); + canvas = new Canvas(newMapContents); + // Scale the translate & matrix to be in gainmap-relative dimensions + canvas.scale(scaleX, scaleY); + canvas.translate(-deviceR.left, -deviceR.top); + canvas.concat(m); + canvas.drawBitmap(sourceGainmap, gSrcR, dstR, paint); + canvas.setBitmap(null); + // Create a new gainmap using a copy of the metadata information from the source but + // with the transformed bitmap created above + return newMapContents; + } + /** * Returns a mutable bitmap with the specified width and height. Its * initial density is as per {@link #getDensity}. The newly created diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java index 9ac84a6159da..f639521ff250 100644 --- a/graphics/java/android/graphics/Gainmap.java +++ b/graphics/java/android/graphics/Gainmap.java @@ -122,6 +122,16 @@ public final class Gainmap implements Parcelable { } /** + * Creates a new gainmap using the provided gainmap as the metadata source and the provided + * bitmap as the replacement for the gainmapContents + * TODO: Make public, it's useful + * @hide + */ + public Gainmap(@NonNull Gainmap gainmap, @NonNull Bitmap gainmapContents) { + this(gainmapContents, nCreateCopy(gainmap.mNativePtr)); + } + + /** * @return Returns the image data of the gainmap represented as a Bitmap. This is represented * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not @@ -325,6 +335,7 @@ public final class Gainmap implements Parcelable { private static native long nGetFinalizer(); private static native long nCreateEmpty(); + private static native long nCreateCopy(long source); private static native void nSetBitmap(long ptr, Bitmap bitmap); diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 8635c56b7bc6..d902fd49ba60 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -93,62 +93,34 @@ <style name="RestartDialogTitleText"> <item name="android:textSize">24sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">8sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> </style> - <style name="RestartDialogBodyText"> + <style name="RestartDialogBodyStyle"> <item name="android:textSize">14sp</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + </style> + + <style name="RestartDialogBodyText" parent="RestartDialogBodyStyle"> <item name="android:letterSpacing">0.02</item> <item name="android:textColor">?android:attr/textColorSecondary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogCheckboxText"> - <item name="android:textSize">16sp</item> + <style name="RestartDialogCheckboxText" parent="RestartDialogBodyStyle"> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">4sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogDismissButton"> + <style name="RestartDialogDismissButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> - <style name="RestartDialogConfirmButton"> + <style name="RestartDialogConfirmButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> <style name="ReachabilityEduHandLayout" parent="Theme.AppCompat.Light"> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java index 7b37d5947f17..57d374b2b8f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -20,6 +20,8 @@ import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.window.TransitionInfo.FLAG_FILLS_TASK; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; +import static com.android.wm.shell.transition.DefaultTransitionHandler.isSupportedOverrideAnimation; + import static java.util.Objects.requireNonNull; import android.content.Context; @@ -113,8 +115,11 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle return false; } final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); - if (options != null && options.getType() == ANIM_SCENE_TRANSITION) { - // Scene-transition will be handled by app side. + if (options != null + // Scene-transition will be handled by app side. + && (options.getType() == ANIM_SCENE_TRANSITION + // Use default transition handler to animate override animation. + || isSupportedOverrideAnimation(options))) { return false; } 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 9fcd207dc370..e1a3f3a1ac5d 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 @@ -55,6 +55,7 @@ import android.util.FloatProperty; import android.util.IntProperty; import android.util.Log; import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -459,7 +460,9 @@ public class BubbleExpandedView extends LinearLayout { if (mManageButton != null) { int visibility = mManageButton.getVisibility(); removeView(mManageButton); - mManageButton = (AlphaOptimizedButton) LayoutInflater.from(getContext()).inflate( + ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate( R.layout.bubble_manage_button, this /* parent */, false /* attach */); addView(mManageButton); mManageButton.setVisibility(visibility); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index d6e1a82a68ff..467e9c7a116b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -375,16 +375,15 @@ public class DisplayLayout { insetsState.getDisplayFrame(), WindowInsets.Type.navigationBars(), false /* ignoreVisibility */); - outInsets.set(insets.left, insets.top, insets.right, insets.bottom); int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); int navBarSize = getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); if (position == NAV_BAR_BOTTOM) { - outInsets.bottom = Math.max(outInsets.bottom , navBarSize); + outInsets.bottom = Math.max(insets.bottom , navBarSize); } else if (position == NAV_BAR_RIGHT) { - outInsets.right = Math.max(outInsets.right , navBarSize); + outInsets.right = Math.max(insets.right , navBarSize); } else if (position == NAV_BAR_LEFT) { - outInsets.left = Math.max(outInsets.left , navBarSize); + outInsets.left = Math.max(insets.left , navBarSize); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index ab8e7e63ef7f..f70d3aec9ec8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -63,8 +63,10 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.InteractionJankMonitorUtils; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; @@ -104,6 +106,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final Rect mWinBounds2 = new Rect(); private final SplitLayoutHandler mSplitLayoutHandler; private final SplitWindowManager mSplitWindowManager; + private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; private final ResizingEffectPolicy mSurfaceEffectPolicy; @@ -128,13 +131,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public SplitLayout(String windowName, Context context, Configuration configuration, SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, - int parallaxType) { + DisplayController displayController, DisplayImeController displayImeController, + ShellTaskOrganizer taskOrganizer, int parallaxType) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); mDensity = configuration.densityDpi; mSplitLayoutHandler = splitLayoutHandler; + mDisplayController = displayController; mDisplayImeController = displayImeController; mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration, parentContainerCallbacks); @@ -145,7 +149,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange updateDividerConfig(mContext); mRootBounds.set(configuration.windowConfiguration.getBounds()); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); resetDividerPosition(); mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide); @@ -314,7 +318,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mRotation = rotation; mDensity = density; mUiMode = uiMode; - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); updateDividerConfig(mContext); initDividerPosition(mTempRect); updateInvisibleRect(); @@ -324,7 +328,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Rotate the layout to specific rotation and calculate new bounds. The stable insets value * should be calculated by display layout. */ - public void rotateTo(int newRotation, Rect stableInsets) { + public void rotateTo(int newRotation) { final int rotationDelta = (newRotation - mRotation + 4) % 4; final boolean changeOrient = (rotationDelta % 2) != 0; @@ -337,7 +341,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // We only need new bounds here, other configuration should be update later. mTempRect.set(mRootBounds); mRootBounds.set(tmpRect); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, stableInsets); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); initDividerPosition(mTempRect); } @@ -548,10 +552,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss); } - private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds, - @Nullable Rect stableInsets) { + private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) { final boolean isLandscape = isLandscape(rootBounds); - final Rect insets = stableInsets != null ? stableInsets : getDisplayInsets(context); + final Rect insets = getDisplayStableInsets(context); // Make split axis insets value same as the larger one to avoid bounds1 and bounds2 // have difference for avoiding size-compat mode when switching unresizable apps in @@ -634,7 +637,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, Consumer<Rect> finishCallback) { final boolean isLandscape = isLandscape(); - final Rect insets = getDisplayInsets(mContext); + final Rect insets = getDisplayStableInsets(mContext); insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top, isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom); @@ -705,13 +708,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return animator; } - private static Rect getDisplayInsets(Context context) { - return context.getSystemService(WindowManager.class) - .getMaximumWindowMetrics() - .getWindowInsets() - .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() - | WindowInsets.Type.displayCutout()) - .toRect(); + private Rect getDisplayStableInsets(Context context) { + final DisplayLayout displayLayout = + mDisplayController.getDisplayLayout(context.getDisplayId()); + return displayLayout != null + ? displayLayout.stableInsets() + : context.getSystemService(WindowManager.class) + .getMaximumWindowMetrics() + .getWindowInsets() + .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() + | WindowInsets.Type.displayCutout()) + .toRect(); } private static boolean isLandscape(Rect bounds) { @@ -784,7 +791,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private int getSmallestWidthDp(Rect bounds) { mTempRect.set(bounds); - mTempRect.inset(getDisplayInsets(mContext)); + mTempRect.inset(getDisplayStableInsets(mContext)); final int minWidth = Math.min(mTempRect.width(), mTempRect.height()); final float density = mContext.getResources().getDisplayMetrics().density; return (int) (minWidth / density); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 5a9c25f7a710..c491fed5d896 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -70,6 +70,8 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; +import com.android.wm.shell.keyguard.KeyguardTransitionHandler; +import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -77,8 +79,6 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.keyguard.KeyguardTransitionHandler; -import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -102,13 +102,13 @@ import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldTransitionHandler; import com.android.wm.shell.windowdecor.WindowDecorViewModel; +import java.util.Optional; + import dagger.BindsOptionalOf; import dagger.Lazy; import dagger.Module; import dagger.Provides; -import java.util.Optional; - /** * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only * accessible from components within the WM subcomponent (can be explicitly exposed to the @@ -548,13 +548,15 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor, - ShellCommandHandler shellCommandHandler) { + ShellCommandHandler shellCommandHandler, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } return new Transitions(context, shellInit, shellController, organizer, pool, - displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler); + displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler, + rootTaskDisplayAreaOrganizer); } @WMSingleton 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 db7c3fc9c9ec..18898f1f2153 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 @@ -438,6 +438,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "startSwipePipToHome: %s, state=%s", componentName, mPipTransitionState); mPipTransitionState.setInSwipePipToHomeTransition(true); sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP); setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo); @@ -450,6 +452,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, SurfaceControl overlay) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "stopSwipePipToHome: %s, state=%s", componentName, mPipTransitionState); // do nothing if there is no startSwipePipToHome being called before if (!mPipTransitionState.getInSwipePipToHomeTransition()) { return; @@ -522,6 +526,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "exitPip: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (isLaunchIntoPipTask()) { exitLaunchIntoPipTask(wct); @@ -559,6 +565,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // destinationBounds calculated above will be incorrect if this is with rotation. wct.setBounds(mToken, null); } else { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "exitPip: %s, dest=%s", mTaskInfo.topActivity, destinationBounds); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, @@ -656,9 +664,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setInterpolator(Interpolators.ALPHA_OUT); animator.start(); mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "removePip: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); } private void removePipImmediately() { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "removePipImmediately: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (Transitions.ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mToken, null); @@ -723,6 +735,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } mPipUiEventLoggerLogger.log(uiEventEnum); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskAppeared: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (mPipTransitionState.getInSwipePipToHomeTransition()) { if (!mWaitForFixedRotation) { onEndOfSwipePipToHomeTransition(); @@ -774,6 +788,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void onTaskAppearedWithFixedRotation(int animationType) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskAppearedWithFixedRotation: %s, state=%s animationType=%d", + mTaskInfo.topActivity, mPipTransitionState, animationType); if (animationType == ANIM_TYPE_ALPHA) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Defer entering PiP alpha animation, fixed rotation is ongoing", TAG); @@ -920,6 +937,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskVanished: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } @@ -961,6 +980,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipBoundsState.setOverrideMinSize( mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo)); final PictureInPictureParams newParams = info.pictureInPictureParams; + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskInfoChanged: %s, state=%s oldParams=%s newParams=%s", + mTaskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, newParams); // mPictureInPictureParams is only null if there is no PiP if (newParams == null || mPictureInPictureParams == null) { @@ -1001,6 +1023,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onFixedRotationStarted(int displayId, int newRotation) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onFixedRotationStarted: %s, state=%s", mTaskInfo, mPipTransitionState); mNextRotation = newRotation; mWaitForFixedRotation = true; @@ -1022,6 +1046,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onFixedRotationFinished(int displayId) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onFixedRotationFinished: %s, state=%s", mTaskInfo, mPipTransitionState); if (!mWaitForFixedRotation) { return; } @@ -1057,6 +1083,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Called when exiting PIP transition is finished to do the state cleanup. */ void onExitPipFinished(TaskInfo info) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onExitPipFinished: %s, state=%s leash=%s", + info.topActivity, mPipTransitionState, mLeash); if (mLeash == null) { // TODO(239461594): Remove once the double call to onExitPipFinished() is fixed ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -1108,6 +1137,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Explicitly set the visibility of PiP window. */ public void setPipVisibility(boolean visible) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "setPipVisibility: %s, state=%s visible=%s", + mTaskInfo.topActivity, mPipTransitionState, visible); if (!isInPip()) { return; } 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 f9d615ad0cf6..bf75132dde54 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 @@ -308,8 +308,9 @@ public class PipTransition extends PipTransitionController { @Override public void end() { Animator animator = mPipAnimationController.getCurrentAnimator(); - if (animator == null) return; - animator.end(); + if (animator != null && animator.isRunning()) { + animator.end(); + } } @Override @@ -815,6 +816,12 @@ public class PipTransition extends PipTransitionController { final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo(); final SurfaceControl leash = pipChange.getLeash(); final int startRotation = pipChange.getStartRotation(); + // Check again in case some callers use startEnterAnimation directly so the flag was not + // set in startAnimation, e.g. from DefaultMixedHandler. + if (!mInFixedRotation) { + mEndFixedRotation = pipChange.getEndFixedRotation(); + mInFixedRotation = mEndFixedRotation != ROTATION_UNDEFINED; + } final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation(); setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, @@ -843,7 +850,7 @@ public class PipTransition extends PipTransitionController { && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash, - sourceHintRect, destinationBounds, rotationDelta, taskInfo); + sourceHintRect, destinationBounds, taskInfo); return; } @@ -934,8 +941,15 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull SurfaceControl leash, @Nullable Rect sourceHintRect, - @NonNull Rect destinationBounds, int rotationDelta, + @NonNull Rect destinationBounds, @NonNull ActivityManager.RunningTaskInfo pipTaskInfo) { + if (mInFixedRotation) { + // If rotation changes when returning to home, the transition should contain both the + // entering PiP and the display change (PipController#startSwipePipToHome has updated + // the display layout to new rotation). So it is not expected to see fixed rotation. + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "%s: SwipePipToHome should not use fixed rotation %d", TAG, mEndFixedRotation); + } final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; if (swipePipToHomeOverlay != null) { // Launcher fade in the overlay on top of the fullscreen Task. It is possible we @@ -946,12 +960,7 @@ public class PipTransition extends PipTransitionController { mPipOrganizer.mSwipePipToHomeOverlay = null; } - Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds(); - if (!Transitions.SHELL_TRANSITIONS_ROTATION && rotationDelta % 2 == 1) { - // PipController#startSwipePipToHome has updated the display layout to new rotation, - // so flip the source bounds to match the same orientation. - sourceBounds = new Rect(0, 0, sourceBounds.height(), sourceBounds.width()); - } + final Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds(); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java index db6138a0891f..b5f44835802f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java @@ -140,6 +140,24 @@ public class PipTransitionState { return state == ENTERING_PIP; } + private String stateToString() { + switch (mState) { + case UNDEFINED: return "undefined"; + case TASK_APPEARED: return "task-appeared"; + case ENTRY_SCHEDULED: return "entry-scheduled"; + case ENTERING_PIP: return "entering-pip"; + case ENTERED_PIP: return "entered-pip"; + case EXITING_PIP: return "exiting-pip"; + } + throw new IllegalStateException("Unknown state: " + mState); + } + + @Override + public String toString() { + return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)", + stateToString(), mInSwipePipToHomeTransition); + } + public interface OnPipTransitionStateChangedListener { void onPipTransitionStateChanged(@TransitionState int oldState, @TransitionState int newState); 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 e7a1395f541c..5e1b6becfa45 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 @@ -31,6 +31,8 @@ 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; @@ -131,6 +133,8 @@ public class PhonePipMenuController implements PipMenuController { private PipMenuView mPipMenuView; + private SurfaceControl mLeash; + private ActionListener mMediaActionListener = new ActionListener() { @Override public void onMediaActionsChanged(List<RemoteAction> mediaActions) { @@ -166,6 +170,7 @@ public class PhonePipMenuController implements PipMenuController { */ @Override public void attach(SurfaceControl leash) { + mLeash = leash; attachPipMenuView(); } @@ -176,6 +181,7 @@ public class PhonePipMenuController implements PipMenuController { public void detach() { hideMenu(); detachPipMenuView(); + mLeash = null; } void attachPipMenuView() { @@ -185,6 +191,36 @@ public class PhonePipMenuController implements PipMenuController { } mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler, mSplitScreenController, 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); @@ -321,30 +357,10 @@ public class PhonePipMenuController implements PipMenuController { return; } - // If there is no pip leash supplied, that means the PiP leash is already finalized - // resizing and the PiP menu is also resized. We then want to do a scale from the current - // new menu bounds. + // TODO(b/286307861) transaction should be applied outside of PiP menu controller if (pipLeash != null && t != null) { - mPipMenuView.getBoundsOnScreen(mTmpSourceBounds); - } else { - mTmpSourceBounds.set(0, 0, destinationBounds.width(), destinationBounds.height()); + t.apply(); } - - mTmpSourceRectF.set(mTmpSourceBounds); - mTmpDestinationRectF.set(destinationBounds); - mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); - final SurfaceControl surfaceControl = getSurfaceControl(); - if (surfaceControl == null) { - return; - } - final SurfaceControl.Transaction menuTx = - mSurfaceControlTransactionFactory.getTransaction(); - menuTx.setMatrix(surfaceControl, mMoveTransform, mTmpTransform); - if (pipLeash != null && t != null) { - // Merge the two transactions, vsyncId has been set on menuTx. - menuTx.merge(t); - } - menuTx.apply(); } /** @@ -362,18 +378,10 @@ public class PhonePipMenuController implements PipMenuController { return; } - final SurfaceControl surfaceControl = getSurfaceControl(); - if (surfaceControl == null) { - return; - } - final SurfaceControl.Transaction menuTx = - mSurfaceControlTransactionFactory.getTransaction(); - menuTx.setCrop(surfaceControl, destinationBounds); + // TODO(b/286307861) transaction should be applied outside of PiP menu controller if (pipLeash != null && t != null) { - // Merge the two transactions, vsyncId has been set on menuTx. - menuTx.merge(t); + t.apply(); } - menuTx.apply(); } private boolean checkPipMenuState() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 6a861ce97431..65727b6145e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -574,6 +574,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityPinned: %s", packageName); mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mAppOpsListener.onActivityPinned(packageName); @@ -585,6 +587,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(mContext); final ComponentName topActivity = topPipActivityInfo.first; + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityUnpinned: %s", topActivity); mTouchHandler.onActivityUnpinned(topActivity); mAppOpsListener.onActivityUnpinned(); mPipInputConsumer.unregisterInputConsumer(); @@ -593,6 +597,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityRestartAttempt: %s", task.topActivity); if (task.getWindowingMode() != WINDOWING_MODE_PINNED) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java index 82fe38ccc7b3..7971c049ff3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java @@ -135,14 +135,14 @@ public class PipSizeSpecHandler { maxWidth = (int) (mOptimizedAspectRatio * shorterLength + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1 + aspectRatio)); - maxHeight = (int) (maxWidth / aspectRatio); + maxHeight = Math.round(maxWidth / aspectRatio); } else { if (aspectRatio > 1f) { maxWidth = shorterLength; - maxHeight = (int) (maxWidth / aspectRatio); + maxHeight = Math.round(maxWidth / aspectRatio); } else { maxHeight = shorterLength; - maxWidth = (int) (maxHeight * aspectRatio); + maxWidth = Math.round(maxHeight * aspectRatio); } } @@ -165,10 +165,9 @@ public class PipSizeSpecHandler { Size maxSize = this.getMaxSize(aspectRatio); - int defaultWidth = Math.max((int) (maxSize.getWidth() * mDefaultSizePercent), + int defaultWidth = Math.max(Math.round(maxSize.getWidth() * mDefaultSizePercent), minSize.getWidth()); - int defaultHeight = Math.max((int) (maxSize.getHeight() * mDefaultSizePercent), - minSize.getHeight()); + int defaultHeight = Math.round(defaultWidth / aspectRatio); return new Size(defaultWidth, defaultHeight); } @@ -188,16 +187,16 @@ public class PipSizeSpecHandler { Size maxSize = this.getMaxSize(aspectRatio); - int minWidth = (int) (maxSize.getWidth() * mMinimumSizePercent); - int minHeight = (int) (maxSize.getHeight() * mMinimumSizePercent); + int minWidth = Math.round(maxSize.getWidth() * mMinimumSizePercent); + int minHeight = Math.round(maxSize.getHeight() * mMinimumSizePercent); // make sure the calculated min size is not smaller than the allowed default min size if (aspectRatio > 1f) { - minHeight = (int) Math.max(minHeight, mDefaultMinSize); - minWidth = (int) (minHeight * aspectRatio); + minHeight = Math.max(minHeight, mDefaultMinSize); + minWidth = Math.round(minHeight * aspectRatio); } else { - minWidth = (int) Math.max(minWidth, mDefaultMinSize); - minHeight = (int) (minWidth / aspectRatio); + minWidth = Math.max(minWidth, mDefaultMinSize); + minHeight = Math.round(minWidth / aspectRatio); } return new Size(minWidth, minHeight); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 8723f9b0181d..a612f5f09e88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -595,9 +595,20 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { cancel(mWillFinishToHome, true /* withScreenshots */, "display change"); return; } - // Don't consider order-only changes as changing apps. - if (!TransitionUtil.isOrderOnly(change)) { + // Don't consider order-only & non-leaf changes as changing apps. + if (!TransitionUtil.isOrderOnly(change) && isLeafTask) { hasChangingApp = true; + } else if (isLeafTask && taskInfo.topActivityType == ACTIVITY_TYPE_HOME + && !mRecentsTask.equals(change.getContainer())) { + // Unless it is a 3p launcher. This means that the 3p launcher was already + // visible (eg. the "pausing" task is translucent over the 3p launcher). + // Treat it as if we are "re-opening" the 3p launcher. + if (openingTasks == null) { + openingTasks = new ArrayList<>(); + openingTaskIsLeafs = new IntArray(); + } + openingTasks.add(change); + openingTaskIsLeafs.add(1); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e616965a29ba..a0bb29b02a2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -122,7 +122,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -171,7 +170,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); - private final DisplayLayout mDisplayLayout; @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -311,7 +309,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, this::onTransitionAnimationComplete, this); mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId)); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -345,7 +342,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainExecutor = mainExecutor; mRecentTasks = recentTasks; mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -1482,6 +1478,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // If running background, we need to reparent current top visible task to main stage. if (!isSplitScreenVisible()) { + // Ensure to evict old splitting tasks because the new split pair might be composed by + // one of the splitting tasks, evicting the task when finishing entering transition + // won't guarantee to put the task to the indicated new position. + mMainStage.evictAllChildren(wct); mMainStage.reparentTopTask(wct); prepareSplitLayout(wct); } @@ -1685,7 +1685,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mRootTaskInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, + mDisplayController, mDisplayImeController, mTaskOrganizer, PARALLAX_ALIGN_CENTER /* parallaxType */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -2149,8 +2149,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (displayId != DEFAULT_DISPLAY) { return; } - mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); - if (mSplitLayout != null && mSplitLayout.isDensityChanged(newConfig.densityDpi) && mMainStage.isActive() && mSplitLayout.updateConfiguration(newConfig) @@ -2167,10 +2165,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { - if (!mMainStage.isActive()) return; + if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return; - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); - mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); + mSplitLayout.rotateTo(toRotation); if (newDisplayAreaInfo != null) { mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration); } @@ -2311,8 +2308,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } else if (!isSplitScreenVisible() && isOpening) { - // If split running backgroud and trigger task is appearing into split, - // prepare to enter split screen. + // If split is running in the background and the trigger task is appearing into + // split, prepare to enter split screen. + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); @@ -2338,6 +2336,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isOpening && getStageOfTask(triggerTask) != null) { // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); @@ -2493,6 +2492,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // handling to the mixed-handler to deal with splitting it up. if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info, startTransaction, finishTransaction, finishCallback)) { + mSplitLayout.update(startTransaction); return true; } } @@ -2877,6 +2877,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Call this when the recents animation canceled during split-screen. */ public void onRecentsInSplitAnimationCanceled() { mPausingTasks.clear(); + setSplitsVisible(false); + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, + true /* reparentLeafTaskIfRelaunch */); + mTaskOrganizer.applyTransaction(wct); } /** Call this when the recents animation during split-screen finishes. */ @@ -2901,8 +2907,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } setSplitsVisible(false); - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct); - logExit(EXIT_REASON_UNKNOWN); + finishWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, + true /* reparentLeafTaskIfRelaunch */); } /** Call this when the recents animation finishes by doing pair-to-pair switch. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 1bbd3679948b..163cf501734c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -61,6 +61,16 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { private TaskViewBase mTaskViewBase; private final Context mContext; + /** + * There could be a situation where we have task info and receive + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}, however, the + * activity might fail to open, and in this case we need to clean up the task view / notify + * listeners of a task removal. This requires task info, so we save the info from onTaskAppeared + * in this situation to allow us to notify listeners correctly if the task failed to open. + */ + private ActivityManager.RunningTaskInfo mPendingInfo; + /* Indicates that the task we attempted to launch in the task view failed to launch. */ + private boolean mTaskNotFound; protected ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; private SurfaceControl mTaskLeash; @@ -236,6 +246,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mTaskInfo = null; mTaskToken = null; mTaskLeash = null; + mPendingInfo = null; + mTaskNotFound = false; } private void updateTaskVisibility() { @@ -257,6 +269,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (isUsingShellTransitions()) { + mPendingInfo = taskInfo; + if (mTaskNotFound) { + // If we were already notified by shell transit that we don't have the + // the task, clean it up now. + cleanUpPendingTask(); + } // Everything else handled by enter transition. return; } @@ -455,6 +473,42 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { return mTaskInfo; } + /** + * Indicates that the task was not found in the start animation for the transition. + * In this case we should clean up the task if we have the pending info. If we don't + * have the pending info, we'll do it when we receive it in + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}. + */ + void setTaskNotFound() { + mTaskNotFound = true; + if (mPendingInfo != null) { + cleanUpPendingTask(); + } + } + + /** + * Called when a task failed to open and we need to clean up task view / + * notify users of task view. + */ + void cleanUpPendingTask() { + if (mPendingInfo != null) { + if (mListener != null) { + final int taskId = mPendingInfo.taskId; + mListenerExecutor.execute(() -> { + mListener.onTaskRemovalStarted(taskId); + }); + } + mTaskViewBase.onTaskVanished(mPendingInfo); + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mPendingInfo.token, false); + + // Make sure the task is removed + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.removeTask(mPendingInfo.token); + mTaskViewTransitions.closeTaskView(wct, this); + } + resetTaskInfo(); + } + void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) { if (mTaskToken == null) { // Nothing to update, task is not yet available @@ -492,6 +546,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { @NonNull SurfaceControl.Transaction finishTransaction, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash, WindowContainerTransaction wct) { + mPendingInfo = null; mTaskInfo = taskInfo; mTaskToken = mTaskInfo.token; mTaskLeash = leash; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 2e7fca3f2b46..5baf2e320227 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -139,7 +139,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { * `taskView`. * @param taskView the pending transition should be for this. */ - private PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { + @VisibleForTesting + PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { for (int i = mPending.size() - 1; i >= 0; --i) { if (mPending.get(i).mTaskView != taskView) continue; if (TransitionUtil.isOpeningType(mPending.get(i).mType)) { @@ -398,10 +399,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } } if (stillNeedsMatchingLaunch) { - throw new IllegalStateException("Expected a TaskView launch in this transition but" - + " didn't get one."); - } - if (wct == null && pending == null && changesHandled != info.getChanges().size()) { + Slog.w(TAG, "Expected a TaskView launch in this transition but didn't get one, " + + "cleaning up the task view"); + // Didn't find a task so the task must have never launched + pending.mTaskView.setTaskNotFound(); + } else if (wct == null && pending == null && changesHandled != info.getChanges().size()) { // Just some house-keeping, let another handler animate. return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 4c678a2bf29b..3bf278cc46cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -58,7 +58,6 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN; -import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition; import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet; import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation; @@ -76,6 +75,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; @@ -103,6 +103,7 @@ import com.android.internal.policy.AttributeCache; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; @@ -137,6 +138,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private final Rect mInsets = new Rect(0, 0, 0, 0); private float mTransitionAnimationScaleSetting = 1.0f; + private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final int mCurrentUserId; private Drawable mEnterpriseThumbnailDrawable; @@ -157,7 +159,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @NonNull DisplayController displayController, @NonNull TransactionPool transactionPool, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, - @NonNull ShellExecutor animExecutor) { + @NonNull ShellExecutor animExecutor, + @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mDisplayController = displayController; mTransactionPool = transactionPool; mContext = context; @@ -168,6 +171,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mCurrentUserId = UserHandle.myUserId(); mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); shellInit.addInitCallback(this::onInit, this); + mRootTDAOrganizer = rootTDAOrganizer; } private void onInit() { @@ -510,10 +514,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } if (backgroundColorForTransition != 0) { - for (int i = 0; i < info.getRootCount(); ++i) { - addBackgroundToTransition(info.getRoot(i).getLeash(), backgroundColorForTransition, - startTransaction, finishTransaction); - } + addBackgroundColorOnTDA(info, backgroundColorForTransition, startTransaction, + finishTransaction); } if (postStartTransactionCallbacks.size() > 0) { @@ -543,6 +545,28 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return true; } + private void addBackgroundColorOnTDA(@NonNull TransitionInfo info, + @ColorInt int color, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + final Color bgColor = Color.valueOf(color); + final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() }; + + for (int i = 0; i < info.getRootCount(); ++i) { + final int displayId = info.getRoot(i).getDisplayId(); + final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder() + .setName("animation-background") + .setCallsite("DefaultTransitionHandler") + .setColorLayer(); + + mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder); + final SurfaceControl backgroundSurface = colorLayerBuilder.build(); + startTransaction.setColor(backgroundSurface, colorArray) + .setLayer(backgroundSurface, -1) + .show(backgroundSurface); + finishTransaction.remove(backgroundSurface); + } + } + private static boolean isDreamTransition(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -696,9 +720,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { if (a != null) { if (!a.isInitialized()) { - final int width = endBounds.width(); - final int height = endBounds.height(); - a.initialize(width, height, width, height); + final Rect animationRange = TransitionUtil.isClosingType(changeMode) + ? change.getStartAbsBounds() : change.getEndAbsBounds(); + a.initialize(animationRange.width(), animationRange.height(), + endBounds.width(), endBounds.height()); } a.restrictDuration(MAX_ANIMATION_DURATION); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); @@ -873,18 +898,31 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } + /** + * Returns {@code true} if the default transition handler can run the override animation. + * @see #loadAnimation(TransitionInfo, TransitionInfo.Change, int, boolean) + */ + public static boolean isSupportedOverrideAnimation( + @NonNull TransitionInfo.AnimationOptions options) { + final int animType = options.getType(); + return animType == ANIM_CUSTOM || animType == ANIM_SCALE_UP + || animType == ANIM_THUMBNAIL_SCALE_UP || animType == ANIM_THUMBNAIL_SCALE_DOWN + || animType == ANIM_CLIP_REVEAL || animType == ANIM_OPEN_CROSS_PROFILE_APPS; + } + private static void applyTransformation(long time, SurfaceControl.Transaction t, - SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix, + SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix, Point position, float cornerRadius, @Nullable Rect immutableClipRect) { - anim.getTransformation(time, transformation); + tmpTransformation.clear(); + anim.getTransformation(time, tmpTransformation); if (position != null) { - transformation.getMatrix().postTranslate(position.x, position.y); + tmpTransformation.getMatrix().postTranslate(position.x, position.y); } - t.setMatrix(leash, transformation.getMatrix(), matrix); - t.setAlpha(leash, transformation.getAlpha()); + t.setMatrix(leash, tmpTransformation.getMatrix(), matrix); + t.setAlpha(leash, tmpTransformation.getAlpha()); final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect); - Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE); + Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE); if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) { // Clip out any overflowing edge extension clipRect.inset(extensionInsets); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index de20c2d90066..cdc82eadcd90 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -69,6 +69,7 @@ import androidx.annotation.BinderThread; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; @@ -265,7 +266,8 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor) { this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor, - mainHandler, animExecutor, null); + mainHandler, animExecutor, null, + new RootTaskDisplayAreaOrganizer(mainExecutor, context)); } public Transitions(@NonNull Context context, @@ -277,7 +279,8 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, - @Nullable ShellCommandHandler shellCommandHandler) { + @Nullable ShellCommandHandler shellCommandHandler, + @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mOrganizer = organizer; mContext = context; mMainExecutor = mainExecutor; @@ -285,7 +288,7 @@ public class Transitions implements RemoteCallable<Transitions>, mDisplayController = displayController; mPlayerImpl = new TransitionPlayerImpl(); mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit, - displayController, pool, mainExecutor, mainHandler, animExecutor); + displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer); mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor); mShellController = shellController; // The very last handler (0 in the list) should be the default one. @@ -641,6 +644,7 @@ public class Transitions implements RemoteCallable<Transitions>, @VisibleForTesting void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { + info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady"); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); final int activeIdx = findByToken(mPendingTransitions, transitionToken); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt new file mode 100644 index 000000000000..f7ce87088040 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt @@ -0,0 +1,200 @@ +/* + * 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.app.Instrumentation +import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils +import android.tools.device.traces.parsers.toFlickerComponent +import androidx.test.filters.RequiresDevice +import androidx.test.uiautomator.By +import androidx.test.uiautomator.BySelector +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME +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 AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : + AutoEnterPipOnGoToHomeTest(flicker) { + private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) + /** Second app used to enter split screen mode */ + protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation) + fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper = + 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() + enterSplitScreen() + // wait until split screen is established + wmHelper + .StateSyncBuilder() + .withWindowSurfaceAppeared(pipApp) + .withWindowSurfaceAppeared(secondAppForSplitScreen) + .withSplitDividerVisible() + .waitForAndVerify() + pipApp.enableAutoEnterForPipActivity() + } + teardown { + // close gracefully so that onActivityUnpinned() can be called before force exit + pipApp.closePipWindow(wmHelper) + pipApp.exit(wmHelper) + secondAppForSplitScreen.exit(wmHelper) + } + transitions { tapl.goHome() } + } + + // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils + private val TIMEOUT_MS = 3_000L + private val overviewSnapshotSelector: BySelector + get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot") + private fun enterSplitScreen() { + // Note: The initial split position in landscape is different between tablet and phone. + // In landscape, tablet will let the first app split to right side, and phone will + // split to left side. + if (tapl.isTablet) { + // TAPL's currentTask on tablet is sometimes not what we expected if the overview + // contains more than 3 task views. We need to use uiautomator directly to find the + // second task to split. + tapl.workspace.switchToOverview().overviewActions.clickSplit() + val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector), + TIMEOUT_MS) + if (snapshots == null || snapshots.size < 1) { + error("Fail to find a overview snapshot to split.") + } + + // Find the second task in the upper right corner in split select mode by sorting + // 'left' in descending order and 'top' in ascending order. + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t2.getVisibleBounds().left - t1.getVisibleBounds().left + } + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t1.getVisibleBounds().top - t2.getVisibleBounds().top + } + snapshots[0].click() + } else { + tapl.workspace + .switchToOverview() + .currentTask + .tapMenu() + .tapSplitMenuItem() + .currentTask + .open() + } + SystemClock.sleep(TIMEOUT_MS) + } + + @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 gesture nav will disappear during transition with alpha animation + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipAppLayerAlwaysVisible() + } + + @Presubmit + @Test + override fun pipWindowRemainInsideVisibleBounds() { + if (tapl.isTablet) { + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } + } else { + // on phones home does not rotate in landscape, PiP enters back to portrait + // orientation so use display bounds from that orientation for assertion + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index ef7bedf49a92..b95732e43357 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -53,7 +53,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { +open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt index 95121def07bc..cdbdb85a9195 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt @@ -62,7 +62,7 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) */ @Presubmit @Test - fun pipWindowRemainInsideVisibleBounds() { + open fun pipWindowRemainInsideVisibleBounds() { flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 3d779481d361..443cea245a4f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -41,6 +42,7 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; @@ -57,6 +59,7 @@ import org.mockito.MockitoAnnotations; public class SplitLayoutTests extends ShellTestCase { @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler; @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; + @Mock DisplayController mDisplayController; @Mock DisplayImeController mDisplayImeController; @Mock ShellTaskOrganizer mTaskOrganizer; @Mock WindowContainerTransaction mWct; @@ -72,6 +75,7 @@ public class SplitLayoutTests extends ShellTestCase { getConfiguration(), mSplitLayoutHandler, mCallbacks, + mDisplayController, mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_NONE)); @@ -100,6 +104,10 @@ public class SplitLayoutTests extends ShellTestCase { // Verify updateConfiguration returns true if the density changed. config.densityDpi = 123; assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration checks the current DisplayLayout + verify(mDisplayController, times(5)) // init * 1 + updateConfiguration * 4 + .getDisplayLayout(anyInt()); } @Test @@ -168,6 +176,14 @@ public class SplitLayoutTests extends ShellTestCase { verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt()); } + @Test + public void testRoateTo_checksDisplayLayout() { + mSplitLayout.rotateTo(90); + + verify(mDisplayController, times(2)) // init * 1 + rotateTo * 1 + .getDisplayLayout(anyInt()); + } + private void waitDividerFlingFinished() { verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(), mRunnableCaptor.capture()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java index 425bbf056b85..1379aedc2284 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java @@ -106,21 +106,21 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { sExpectedDefaultSizes = new HashMap<>(); sExpectedMinSizes = new HashMap<>(); - sExpectedMaxSizes.put(16f / 9, new Size(1000, 562)); - sExpectedDefaultSizes.put(16f / 9, new Size(600, 337)); - sExpectedMinSizes.put(16f / 9, new Size(499, 281)); + sExpectedMaxSizes.put(16f / 9, new Size(1000, 563)); + sExpectedDefaultSizes.put(16f / 9, new Size(600, 338)); + sExpectedMinSizes.put(16f / 9, new Size(501, 282)); sExpectedMaxSizes.put(4f / 3, new Size(892, 669)); sExpectedDefaultSizes.put(4f / 3, new Size(535, 401)); - sExpectedMinSizes.put(4f / 3, new Size(445, 334)); + sExpectedMinSizes.put(4f / 3, new Size(447, 335)); sExpectedMaxSizes.put(3f / 4, new Size(669, 892)); sExpectedDefaultSizes.put(3f / 4, new Size(401, 535)); - sExpectedMinSizes.put(3f / 4, new Size(334, 445)); + sExpectedMinSizes.put(3f / 4, new Size(335, 447)); sExpectedMaxSizes.put(9f / 16, new Size(562, 999)); sExpectedDefaultSizes.put(9f / 16, new Size(337, 599)); - sExpectedMinSizes.put(9f / 16, new Size(281, 499)); + sExpectedMinSizes.put(9f / 16, new Size(281, 500)); } private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 81fc8438eec0..1b389565c066 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -128,8 +128,8 @@ public class TaskViewTest extends ShellTestCase { doReturn(true).when(mTransitions).isRegistered(); } mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions)); - mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer, - mTaskViewTransitions, mSyncQueue); + mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer, + mTaskViewTransitions, mSyncQueue)); mTaskView = new TaskView(mContext, mTaskViewTaskController); mTaskView.setListener(mExecutor, mViewListener); } @@ -544,4 +544,23 @@ public class TaskViewTest extends ShellTestCase { mTaskView.removeTask(); verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); } + + @Test + public void testOnTaskAppearedWithTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.setTaskNotFound(); + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + + verify(mTaskViewTaskController).cleanUpPendingTask(); + verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); + } + + @Test + public void testOnTaskAppeared_withoutTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + verify(mTaskViewTaskController, never()).cleanUpPendingTask(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 71ad0d79eaca..03ed18c86608 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -17,6 +17,7 @@ package com.android.wm.shell.taskview; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.google.common.truth.Truth.assertThat; @@ -25,16 +26,19 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.graphics.Rect; +import android.os.IBinder; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.transition.Transitions; @@ -45,6 +49,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.List; @SmallTest @@ -295,4 +300,34 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, new Rect(0, 0, 100, 100)); } + + @Test + public void test_startAnimation_setsTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + when(change.getTaskInfo()).thenReturn(mTaskInfo); + when(change.getMode()).thenReturn(TRANSIT_OPEN); + + List<TransitionInfo.Change> changes = new ArrayList<>(); + changes.add(change); + + TransitionInfo info = mock(TransitionInfo.class); + when(info.getChanges()).thenReturn(changes); + + mTaskViewTransitions.startTaskView(new WindowContainerTransaction(), + mTaskViewTaskController, + mock(IBinder.class)); + + TaskViewTransitions.PendingTransition pending = + mTaskViewTransitions.findPendingOpeningTransition(mTaskViewTaskController); + + mTaskViewTransitions.startAnimation(pending.mClaimed, + info, + new SurfaceControl.Transaction(), + new SurfaceControl.Transaction(), + mock(Transitions.TransitionFinishCallback.class)); + + verify(mTaskViewTaskController).setTaskNotFound(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 963632b1f8f6..961e3e9c5735 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -51,7 +51,6 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.after; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; @@ -93,7 +92,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.server.testutils.StubTransaction; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.TransitionInfoBuilder; @@ -105,6 +103,7 @@ import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellSharedConstants; +import com.android.wm.shell.util.StubTransaction; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java new file mode 100644 index 000000000000..855f5416fd0f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java @@ -0,0 +1,313 @@ +/* + * 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.wm.shell.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.HardwareBuffer; +import android.os.IBinder; +import android.os.Parcel; +import android.view.InputWindowHandle; +import android.view.Surface; +import android.view.SurfaceControl; + +import java.util.HashSet; +import java.util.concurrent.Executor; + +/** + * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit + * testing to avoid calls to native code. + * + * Note: This is a copy of com.android.server.testutils.StubTransaction + */ +public class StubTransaction extends SurfaceControl.Transaction { + + private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>(); + + @Override + public void apply() { + for (Runnable listener : mWindowInfosReportedListeners) { + listener.run(); + } + } + + @Override + public void close() { + } + + @Override + public void apply(boolean sync) { + apply(); + } + + @Override + public SurfaceControl.Transaction setVisibility(SurfaceControl sc, boolean visible) { + return this; + } + + @Override + public SurfaceControl.Transaction show(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction hide(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl sc, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setBufferSize(SurfaceControl sc, + int w, int h) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayer(SurfaceControl sc, int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, + int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setTransparentRegionHint(SurfaceControl sc, + Region transparentRegion) { + return this; + } + + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl sc, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setInputWindowInfo(SurfaceControl sc, + InputWindowHandle handle) { + return this; + } + + @Override + public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop, + Rect destFrame, @Surface.Rotation int orientation) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, + float dsdx, float dtdx, float dtdy, float dsdy) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorTransform(SurfaceControl sc, float[] matrix, + float[] translation) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, Rect crop) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, int width, int height) { + return this; + } + + @Override + @NonNull + public SurfaceControl.Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) { + return this; + } + + @Override + public SurfaceControl.Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { + return this; + } + + @Override + public SurfaceControl.Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayerStack(SurfaceControl sc, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { + return this; + } + + @Override + public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) { + return this; + } + + @Override + public SurfaceControl.Transaction setSecure(SurfaceControl sc, boolean isSecure) { + return this; + } + + @Override + public SurfaceControl.Transaction setOpaque(SurfaceControl sc, boolean isOpaque) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySurface(IBinder displayToken, Surface surface) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken, + int orientation, Rect layerStackRect, Rect displayRect) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySize(IBinder displayToken, int width, int height) { + return this; + } + + @Override + public SurfaceControl.Transaction setAnimationTransaction() { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(SurfaceControl sc, int key, int data) { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(SurfaceControl sc, int key, Parcel data) { + return this; + } + + @Override + public SurfaceControl.Transaction merge(SurfaceControl.Transaction other) { + return this; + } + + @Override + public SurfaceControl.Transaction remove(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor, + SurfaceControl.TransactionCommittedListener listener) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorSpaceAgnostic(SurfaceControl sc, boolean agnostic) { + return this; + } + + @Override + public SurfaceControl.Transaction setFrameRateSelectionPriority(SurfaceControl sc, + int priority) { + return this; + } + + @Override + public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate, + int compatibility, int changeFrameRateStrategy) { + return this; + } + + @Override + public SurfaceControl.Transaction unsetColor(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) { + return this; + } + + @Override + public SurfaceControl.Transaction setFixedTransformHint(SurfaceControl sc, + @Surface.Rotation int transformHint) { + return this; + } + + @Override + public SurfaceControl.Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) { + return this; + } + + @Override + @NonNull + public SurfaceControl.Transaction setBuffer(@NonNull SurfaceControl sc, + @Nullable HardwareBuffer buffer) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) { + return this; + } + + @Override + public SurfaceControl.Transaction setTrustedOverlay(SurfaceControl sc, + boolean isTrustedOverlay) { + return this; + } + + @Override + public SurfaceControl.Transaction addWindowInfosReportedListener(@NonNull Runnable listener) { + mWindowInfosReportedListeners.add(listener); + return this; + } +} diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index 3527eeead1d5..2a6dc7b95c07 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -108,7 +108,7 @@ status_t CursorWindow::maybeInflate() { { // Migrate existing contents into new ashmem region - uint32_t slotsSize = mSize - mSlotsOffset; + uint32_t slotsSize = sizeOfSlots(); uint32_t newSlotsOffset = mInflatedSize - slotsSize; memcpy(static_cast<uint8_t*>(newData), static_cast<uint8_t*>(mData), mAllocOffset); @@ -216,11 +216,9 @@ status_t CursorWindow::writeToParcel(Parcel* parcel) { if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail; } else { // Since we know we're going to be read-only on the remote side, - // we can compact ourselves on the wire, with just enough padding - // to ensure our slots stay aligned - size_t slotsSize = mSize - mSlotsOffset; - size_t compactedSize = mAllocOffset + slotsSize; - compactedSize = (compactedSize + 3) & ~3; + // we can compact ourselves on the wire. + size_t slotsSize = sizeOfSlots(); + size_t compactedSize = sizeInUse(); if (parcel->writeUint32(compactedSize)) goto fail; if (parcel->writeBool(false)) goto fail; void* dest = parcel->writeInplace(compactedSize); diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h index 6e55a9a0eb8b..9ec026a19c4c 100644 --- a/libs/androidfw/include/androidfw/CursorWindow.h +++ b/libs/androidfw/include/androidfw/CursorWindow.h @@ -90,6 +90,9 @@ public: inline uint32_t getNumRows() { return mNumRows; } inline uint32_t getNumColumns() { return mNumColumns; } + inline size_t sizeOfSlots() const { return mSize - mSlotsOffset; } + inline size_t sizeInUse() const { return mAllocOffset + sizeOfSlots(); } + status_t clear(); status_t setNumColumns(uint32_t numColumns); diff --git a/libs/androidfw/tests/CursorWindow_test.cpp b/libs/androidfw/tests/CursorWindow_test.cpp index d1cfd03276c2..3cdb31e982b1 100644 --- a/libs/androidfw/tests/CursorWindow_test.cpp +++ b/libs/androidfw/tests/CursorWindow_test.cpp @@ -21,9 +21,16 @@ #include "TestHelpers.h" +// Verify that the memory in use is a multiple of 4 bytes +#define ASSERT_ALIGNED(w) \ + ASSERT_EQ(((w)->sizeInUse() & 3), 0); \ + ASSERT_EQ(((w)->freeSpace() & 3), 0); \ + ASSERT_EQ(((w)->sizeOfSlots() & 3), 0) + #define CREATE_WINDOW_1K \ CursorWindow* w; \ - CursorWindow::create(String8("test"), 1 << 10, &w); + CursorWindow::create(String8("test"), 1 << 10, &w); \ + ASSERT_ALIGNED(w); #define CREATE_WINDOW_1K_3X3 \ CursorWindow* w; \ @@ -31,11 +38,13 @@ ASSERT_EQ(w->setNumColumns(3), OK); \ ASSERT_EQ(w->allocRow(), OK); \ ASSERT_EQ(w->allocRow(), OK); \ - ASSERT_EQ(w->allocRow(), OK); + ASSERT_EQ(w->allocRow(), OK); \ + ASSERT_ALIGNED(w); #define CREATE_WINDOW_2M \ CursorWindow* w; \ - CursorWindow::create(String8("test"), 1 << 21, &w); + CursorWindow::create(String8("test"), 1 << 21, &w); \ + ASSERT_ALIGNED(w); static constexpr const size_t kHalfInlineSize = 8192; static constexpr const size_t kGiantSize = 1048576; @@ -49,6 +58,7 @@ TEST(CursorWindowTest, Empty) { ASSERT_EQ(w->getNumColumns(), 0); ASSERT_EQ(w->size(), 1 << 10); ASSERT_EQ(w->freeSpace(), 1 << 10); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, SetNumColumns) { @@ -60,6 +70,7 @@ TEST(CursorWindowTest, SetNumColumns) { ASSERT_NE(w->setNumColumns(5), OK); ASSERT_NE(w->setNumColumns(3), OK); ASSERT_EQ(w->getNumColumns(), 4); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, SetNumColumnsAfterRow) { @@ -70,6 +81,7 @@ TEST(CursorWindowTest, SetNumColumnsAfterRow) { ASSERT_EQ(w->allocRow(), OK); ASSERT_NE(w->setNumColumns(4), OK); ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, AllocRow) { @@ -83,14 +95,17 @@ TEST(CursorWindowTest, AllocRow) { ASSERT_EQ(w->allocRow(), OK); ASSERT_LT(w->freeSpace(), before); ASSERT_EQ(w->getNumRows(), 1); + ASSERT_ALIGNED(w); // Verify we can unwind ASSERT_EQ(w->freeLastRow(), OK); ASSERT_EQ(w->freeSpace(), before); ASSERT_EQ(w->getNumRows(), 0); + ASSERT_ALIGNED(w); // Can't unwind when no rows left ASSERT_NE(w->freeLastRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, AllocRowBounds) { @@ -100,6 +115,7 @@ TEST(CursorWindowTest, AllocRowBounds) { ASSERT_EQ(w->setNumColumns(60), OK); ASSERT_EQ(w->allocRow(), OK); ASSERT_NE(w->allocRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreNull) { @@ -116,6 +132,7 @@ TEST(CursorWindowTest, StoreNull) { auto field = w->getFieldSlot(0, 0); ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreLong) { @@ -134,6 +151,7 @@ TEST(CursorWindowTest, StoreLong) { ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreString) { @@ -155,6 +173,7 @@ TEST(CursorWindowTest, StoreString) { auto actual = w->getFieldSlotValueString(field, &size); ASSERT_EQ(std::string(actual), "cafe"); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreBounds) { @@ -175,6 +194,7 @@ TEST(CursorWindowTest, StoreBounds) { ASSERT_EQ(w->getFieldSlot(-1, 0), nullptr); ASSERT_EQ(w->getFieldSlot(0, -1), nullptr); ASSERT_EQ(w->getFieldSlot(-1, -1), nullptr); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, Inflate) { @@ -234,6 +254,7 @@ TEST(CursorWindowTest, Inflate) { ASSERT_NE(actual, buf); ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelEmpty) { @@ -249,10 +270,12 @@ TEST(CursorWindowTest, ParcelEmpty) { ASSERT_EQ(w->getNumColumns(), 0); ASSERT_EQ(w->size(), 0); ASSERT_EQ(w->freeSpace(), 0); + ASSERT_ALIGNED(w); // We can't mutate the window after parceling ASSERT_NE(w->setNumColumns(4), OK); ASSERT_NE(w->allocRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelSmall) { @@ -311,6 +334,7 @@ TEST(CursorWindowTest, ParcelSmall) { ASSERT_EQ(actualSize, 0); ASSERT_NE(actual, nullptr); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelLarge) { @@ -364,6 +388,7 @@ TEST(CursorWindowTest, ParcelLarge) { ASSERT_EQ(actualSize, 0); ASSERT_NE(actual, nullptr); } + ASSERT_ALIGNED(w); } } // android diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp index 0f8a85dd9e62..cec0ee7ee247 100644 --- a/libs/hwui/jni/Gainmap.cpp +++ b/libs/hwui/jni/Gainmap.cpp @@ -86,6 +86,16 @@ jlong Gainmap_createEmpty(JNIEnv*, jobject) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap)); } +jlong Gainmap_createCopy(JNIEnv*, jobject, jlong sourcePtr) { + Gainmap* gainmap = new Gainmap(); + gainmap->incStrong(0); + if (sourcePtr) { + Gainmap* src = fromJava(sourcePtr); + gainmap->info = src->info; + } + return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap)); +} + static void Gainmap_setBitmap(JNIEnv* env, jobject, jlong gainmapPtr, jobject jBitmap) { android::Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, jBitmap); fromJava(gainmapPtr)->bitmap = sk_ref_sp(bitmap); @@ -237,6 +247,7 @@ static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, job static const JNINativeMethod gGainmapMethods[] = { {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer}, {"nCreateEmpty", "()J", (void*)Gainmap_createEmpty}, + {"nCreateCopy", "(J)J", (void*)Gainmap_createCopy}, {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*)Gainmap_setBitmap}, {"nSetRatioMin", "(JFFF)V", (void*)Gainmap_setRatioMin}, {"nGetRatioMin", "(J[F)V", (void*)Gainmap_getRatioMin}, diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 32680dae75b9..38bb447f8a16 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -359,6 +359,51 @@ import java.util.concurrent.locks.ReentrantLock; codec to use a previously created {@linkplain #createPersistentInputSurface persistent input surface} by calling {@link #setInputSurface}. + <h4 id=EncoderProfiles><a name="EncoderProfiles"></a>Encoder Profiles</h4> + <p> + When using an encoder, it is recommended to set the desired codec {@link MediaFormat#KEY_PROFILE + profile} during {@link #configure configure()}. (This is only meaningful for + {@link MediaFormat#KEY_MIME media formats} for which profiles are defined.) + <p> + If a profile is not specified during {@code configure}, the encoder will choose a profile for the + session based on the available information. We will call this value the <i>default profile</i>. + The selection of the default profile is device specific and may not be deterministic + (could be ad hoc or even experimental). The encoder may choose a default profile that is not + suitable for the intended encoding session, which may result in the encoder ultimately rejecting + the session. + <p> + The encoder may reject the encoding session if the configured (or default if unspecified) profile + does not support the codec input (mainly the {@link MediaFormat#KEY_COLOR_FORMAT color format} for + video/image codecs, or the {@link MediaFormat#KEY_PCM_ENCODING sample encoding} and the {@link + MediaFormat#KEY_CHANNEL_COUNT number of channels} for audio codecs, but also possibly + {@link MediaFormat#KEY_WIDTH width}, {@link MediaFormat#KEY_HEIGHT height}, + {@link MediaFormat#KEY_FRAME_RATE frame rate}, {@link MediaFormat#KEY_BIT_RATE bitrate} or + {@link MediaFormat#KEY_SAMPLE_RATE sample rate}.) + Alternatively, the encoder may choose to (but is not required to) convert the input to support the + selected (or default) profile - or adjust the chosen profile based on the presumed or detected + input format - to ensure a successful encoding session. <b>Note</b>: Converting the input to match + an incompatible profile will in most cases result in decreased codec performance. + <p> + To ensure backward compatibility, the following guarantees are provided by Android: + <ul> + <li>The default video encoder profile always supports 8-bit YUV 4:2:0 color format ({@link + CodecCapabilities#COLOR_FormatYUV420Flexible COLOR_FormatYUV420Flexible} and equivalent + {@link CodecCapabilities#colorFormats supported formats}) for both Surface and ByteBuffer modes. + <li>The default video encoder profile always supports the default 8-bit RGBA color format in + Surface mode even if no such formats are enumerated in the {@link CodecCapabilities#colorFormats + supported formats}. + </ul> + <p class=note> + <b>Note</b>: the accepted profile can be queried through the {@link #getOutputFormat output + format} of the encoder after {@code configure} to allow applications to set up their + codec input to a format supported by the encoder profile. + <p> + <b>Implication:</b> + <ul> + <li>Applications that want to encode 4:2:2, 4:4:4, 10+ bit or HDR video input <b>MUST</b> configure + a suitable profile for encoders. + </ul> + <h4 id=CSD><a name="CSD"></a>Codec-specific Data</h4> <p> Some formats, notably AAC audio and MPEG4, H.264 and H.265 video formats require the actual data diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index b1b7d4000635..46db77708521 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1136,11 +1136,14 @@ public final class MediaFormat { * may fail if other parameters are not compatible with the desired * profile or if the desired profile is not supported, but it may also * fail silently (where the encoder ends up using a different, compatible profile.) + * <p> + * It is recommended that the profile is set for all encoders. For more information, see + * the <i>Encoder Profiles</i> section of the {@link MediaCodec} API reference. * <p class="note"> * <strong>Note:</strong> Codecs are free to use all the available * coding tools at the specified profile, but may ultimately choose to not do so. * <p class="note"> - * <strong>Note:</strong> When configuring video encoders, profile must be + * <strong>Note:</strong> When configuring video encoders, profile (if set) must be * set together with {@link #KEY_LEVEL level}. * * @see MediaCodecInfo.CodecCapabilities#profileLevels diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 8128d8a64c7b..b131a2fa53af 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -31,7 +31,7 @@ <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용"</string> + <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>이 휴대전화에서 이 정보에 액세스하도록 허용"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 서비스"</string> <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 휴대전화의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string> diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml index 2cbfce8a42f7..0e170254ab4b 100644 --- a/packages/CredentialManager/res/values-vi/strings.xml +++ b/packages/CredentialManager/res/values-vi/strings.xml @@ -61,7 +61,7 @@ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu"</string> <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> khoá truy cập"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> thông tin xác thực"</string> - <string name="passkey_before_subtitle" msgid="2448119456208647444">"Mã xác thực"</string> + <string name="passkey_before_subtitle" msgid="2448119456208647444">"Khoá truy cập"</string> <string name="another_device" msgid="5147276802037801217">"Thiết bị khác"</string> <string name="other_password_manager" msgid="565790221427004141">"Trình quản lý mật khẩu khác"</string> <string name="close_sheet" msgid="1393792015338908262">"Đóng trang tính"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 2dafbcb95205..a78509d897aa 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -66,8 +66,9 @@ class CredentialManagerRepo( ) val originName: String? = when (requestInfo?.type) { - RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin - RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin + RequestInfo.TYPE_CREATE -> processHttpsOrigin( + requestInfo.createCredentialRequest?.origin) + RequestInfo.TYPE_GET -> processHttpsOrigin(requestInfo.getCredentialRequest?.origin) else -> null } @@ -247,6 +248,9 @@ class CredentialManagerRepo( } companion object { + private const val HTTPS = "https://" + private const val FORWARD_SLASH = "/" + fun sendCancellationCode( cancelCode: Int, requestToken: IBinder?, @@ -266,5 +270,17 @@ class CredentialManagerRepo( CancelUiRequest::class.java ) } + + /** Removes "https://", and the trailing slash if present for an https request. */ + private fun processHttpsOrigin(origin: String?): String? { + var processed = origin + if (processed?.startsWith(HTTPS) == true) { // Removes "https://" + processed = processed.substring(HTTPS.length) + if (processed?.endsWith(FORWARD_SLASH) == true) { // Removes the trailing slash + processed = processed.substring(0, processed.length - 1) + } + } + return processed + } } } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 119bb73bb96e..98ad6e33ce3d 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Ontkoppel"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ontkoppel tans…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Verbind tans…"</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Verbind tans …"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Gekoppel<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Verbind tans…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Gekoppel (geen foon nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Hierdie foon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Hierdie tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokluidspreker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Gekoppelde toestel"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 89d68c32faee..b8b1b69b8054 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ይህ ስልክ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ይህ ጡባዊ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"የመትከያ ድምጽ ማውጫ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"የተገናኘ መሣሪያ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"በዚህ መሣሪያ ላይ ማጫወት አልተቻለም"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ለመቀየር መለያ ያልቁ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index f7e2b2d0e71a..18dc07f92b52 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"هذا الهاتف"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"هذا الجهاز اللوحي"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"مكبّر صوت بقاعدة إرساء"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"جهاز متّصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"لا يمكن تشغيل الوسائط هنا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"يجب ترقية الحساب للتبديل"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 594ed7ba6311..725a0572e70d 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফ’নটো"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই টেবলেটটো"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ড’ক স্পীকাৰ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"সংযোগ হৈ থকা ডিভাইচ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 0b4779fb9893..ec1b8cfe406c 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu planşet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok dinamiki"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Qoşulmuş cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oxutmaq olmur"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Keçirmək üçün hesabı güncəllə"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 298835fa6724..91ca8a66a9e1 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik bazne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne možete da pustite na ovom uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite nalog radi prebacivanja"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 666dbb15d2c8..aa28a3158abb 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -304,7 +304,7 @@ <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Пры выбары сеткі Wi-Fi указваць у журнале RSSI для кожнага SSID"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Зніжае расход зараду акумулятара і павышае прадукцыйнасць мабільных сетак"</string> <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Калі ўключаны гэты рэжым, MAC-адрас гэтай прылады можа змяняцца падчас кожнага падключэння да сеткі з актыўнай рандамізацыяй MAC-адрасоў."</string> - <string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з улікам трафіка"</string> + <string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з падлікам трафіка"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"Сетка без уліку трафіка"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Памеры буфера журнала"</string> <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Выберыце памеры сродку вядзення журнала для буфераў журнала"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 94466348282f..2779689c41ad 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Този телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Този таблет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Високоговорител докинг станция"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index a27c6ff3c52b..68283ca69234 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ডিসকানেক্ট হচ্ছে..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"কানেক্ট হচ্ছে..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"কানেক্ট করা আছে<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"যুক্ত করা হচ্ছে..."</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"পেয়ার করা হচ্ছে..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"কানেক্ট করা আছে (ফোনের অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"কানেক্ট করা আছে (মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"কানেক্ট করা আছে (ফোনের বা মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফোন"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই ট্যাবলেট"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ডক স্পিকার"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"কানেক্ট থাকা ডিভাইস"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইসে চালানো যাবে না"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"পাল্টাতে অ্যাকাউন্ট আপগ্রেড করুন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 345cad1792d3..8e589f6e78fe 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nije moguće reproducirati na uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite račun da promijenite"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 899bc7d49cfd..f22b489b839b 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Aquest telèfon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Aquesta tauleta"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base d\'altaveu"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositiu connectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No es pot reproduir en aquest dispositiu"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualitza el compte per canviar"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 1c8fe92fb3e2..363c22e72aaf 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok s reproduktorem"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 92531487b2b8..1df4deeeddd2 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Denne tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockhøjttaler"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Forbundet enhed"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke afspilles på denne enhed"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Opgrader kontoen for at skifte"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 64e9bcf9389f..8f7518012def 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Αυτό το tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Ηχείο βάσης σύνδεσης"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 6779e89e572d..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 6779e89e572d..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 6779e89e572d..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 8946365051d7..4e6cd004268f 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Conector de la bocina"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 82686825d521..6ffed25cb768 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altavoz base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 8e30e3dce29c..434e7d712cdc 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"See telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"See tahvelarvuti"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doki kõlar"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ühendatud seade"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Selles seadmes ei saa esitada"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lülitamiseks täiendage kontot"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 7d64cfaaf66b..591d3cf1e52c 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -99,8 +99,8 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ezk. gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Esk- gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktibo"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerreko audifonoa soilik"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuineko audifonoa soilik"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerrekoa soilik"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuinekoa soilik"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktibo, ezkerreko eta eskuineko audifonoak"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Euskarriaren audioa"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefono-deiak"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefono hau"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tableta hau"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Oinarri bozgorailuduna"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Konektatutako gailua"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ezin da erreproduzitu gailu honetan"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aldatzeko, bertsio-berritu kontua"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 3ba5436e98a9..b4e9b350432f 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -231,9 +231,9 @@ <string name="adb_wireless_error" msgid="721958772149779856">"خطا"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"اشکالزدایی بیسیم"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"برای مشاهده و استفاده از دستگاههای در دسترس، اشکالزدایی بیسیم را روشن کنید"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با رمزینه پاسخسریع"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"جفت کردن دستگاه با رمزینه پاسخسریع"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر رمزینه پاسخسریع مرتبط کنید"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"مرتبط کردن دستگاه با کد مرتبطسازی"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"جفت کردن دستگاه با کد مرتبطسازی"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"دستگاههای جدید را با استفاده از کد شش رقمی مرتبط کنید"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"دستگاههای مرتبطشده"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"درحالحاضر متصل"</string> @@ -242,13 +242,13 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"اثرانگشت دستگاه: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"اتصال ناموفق"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"مطمئن شوید که <xliff:g id="DEVICE_NAME">%1$s</xliff:g> به شبکه درستی متصل شده باشد"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"مرتبط کردن با دستگاه"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"جفت کردن با دستگاه"</string> <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"کد مرتبطسازی Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"مرتبطسازی ناموفق"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"مطمئن شوید که دستگاه به همان شبکه متصل باشد."</string> <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"مرتبطسازی دستگاه…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"جفت کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"نشانی IP و درگاه"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن رمزینه پاسخسریع"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"این تلفن"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"این رایانه لوحی"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"بلندگوی پایه اتصال"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"دستگاه متصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"نمیتوان در این دستگاه پخش کرد"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"برای تغییر، حساب را ارتقا دهید"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index d1bf50a106a2..b3a7560e128c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tämä puhelin"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tämä tabletti"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Telinekaiutin"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Yhdistetty laite"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ei voi toistaa tällä laitteella"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Vaihda päivittämällä tili"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 11579af82206..8d3eae4003ab 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur du socle"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de faire jouer le contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à jour le compte pour passer à la version payante"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index cf444a6d67b5..a4e43c7cb0ef 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur station d\'accueil"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de lire du contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à niveau le compte pour changer"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 89145cbb1697..dbc0ba618892 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tableta"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altofalante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Non se pode reproducir contido neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Cambia a conta a un plan superior para facer a modificación"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index fbcc84a79c2a..ee44c0aa4a53 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"આ ફોન"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"આ ટૅબ્લેટ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ડૉક સ્પીકર"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"કનેક્ટ કરેલું ડિવાઇસ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index a123a854a529..b97837bf9eba 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यह फ़ोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यह टैबलेट"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट किया गया डिवाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 4566cadd162a..6acfb9545c7b 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne može se reproducirati ovdje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite i prebacite se"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index b9f9d799099b..14ecad0339f0 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ez a telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ez a táblagép"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhangszóró"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Csatlakoztatott eszköz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nem játszható le ezen az eszközön"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"A váltáshoz frissítse fiókját"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index de002a0c17dd..5083165e5fa7 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Այս հեռախոսը"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Այս պլանշետը"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Դոկ-կայանով բարձրախոս"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Միացված սարք"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Հնարավոր չէ նվագարկել այս սարքում"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Փոխելու համար անցեք հաշվի պրեմիում տարբերակին"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index cdc297e21df0..c5e69126b0b3 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ponsel ini"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker dok"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Perangkat yang terhubung"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat memutar di perangkat ini"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade akun untuk beralih"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 18acafd412df..5d1893088281 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Þessi sími"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Þessi spjaldtölva"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Hátalaradokka"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tengt tæki"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ekki er hægt að spila í þessu tæki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppfærðu reikninginn til að skipta"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 7e84c5a9b21a..85d4985cc50e 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Questo telefono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Questo tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base con altoparlante"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index c772f375c915..af56031e7901 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"הטלפון הזה"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"הטאבלט הזה"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"הרמקול של אביזר העגינה"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index a459abfe2429..076d0223df9c 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -551,7 +551,7 @@ <skip /> <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> <skip /> - <string name="media_transfer_this_phone" msgid="7194341457812151531">"このスマートフォン"</string> + <string name="media_transfer_this_phone" msgid="7194341457812151531">"このデバイス"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"このデバイスでは再生できません"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"アカウントを更新して切り替えてください"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"再生不可: ダウンロードしたコンテンツ"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index eba3e9e2872a..147512b2cc20 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ეს ტელეფონი"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ეს ტაბლეტი"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"დინამიკის სამაგრი"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"დაკავშირებული მოწყობილობა"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ამ მოწყობილობაზე დაკვრა შეუძლებელია"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"გადასართავად განაახლეთ ანგარიში"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 83388a09b3b6..7df9fb329b43 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылған"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылуда…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Жалғауда..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Қосылып жатыр..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Жұптасып жатыр..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (телефонсыз)"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Осы телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Осы планшет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Қондыру динамигі"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Жалғанған құрылғы"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Осы құрылғыда ойнату мүмкін емес."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Ауысу үшін аккаунтты жаңартыңыз."</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 0e470f51d433..81d26692a20e 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"សកម្ម"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"សកម្មខាងឆ្វេងតែប៉ុណ្ណោះ"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"សកម្ម ខាងឆ្វេងតែប៉ុណ្ណោះ"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"សកម្មខាងស្ដាំតែប៉ុណ្ណោះ"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"សកម្មខាងឆ្វេង និងស្ដាំ"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"សំឡេងមេឌៀ"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ថេប្លេតនេះ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ឧបករណ៍បំពងសំឡេងដែលមានជើងភ្ជាប់"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ឧបករណ៍ដែលបានភ្ជាប់"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"មិនអាចចាក់នៅលើឧបករណ៍នេះបានទេ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ដំឡើងកម្រិតគណនី ដើម្បីប្ដូរ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a66f83c54b8e..f7f6a2f22ce2 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -49,7 +49,7 @@ <string name="wifi_security_none_owe" msgid="5241745828327404101">"ಯಾವುದೂ ಇಲ್ಲ/Enhanced Open"</string> <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string> <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-ಎಂಟರ್ಪ್ರೈಸ್ 192-ಬಿಟ್"</string> - <string name="wifi_remembered" msgid="3266709779723179188">"ಉಳಿಸಲಾಗಿದೆ"</string> + <string name="wifi_remembered" msgid="3266709779723179188">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ಡಾಕ್ ಸ್ಪೀಕರ್"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ಕನೆಕ್ಟ್ ಮಾಡಿರುವ ಸಾಧನ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ಬದಲಾಯಿಸಲು ಖಾತೆಯನ್ನು ಅಪ್ಗ್ರೇಡ್ ಮಾಡಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 273176ce17dc..ffa05cc91bd1 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"이 휴대전화"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"태블릿"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"도크 스피커"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"연결된 기기"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"이 기기에서 재생할 수 없음"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"전환하려면 계정을 업그레이드하세요."</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 3a75a8e5b3da..58a930f304de 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -231,7 +231,7 @@ <string name="adb_wireless_error" msgid="721958772149779856">"Ката"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Жеткиликтүү түзмөктөрдү көрүү үчүн мүчүлүштүктөрдү Wi-Fi аркылуу аныктоону күйгүзүңүз"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Түзмөктү QR коду аркылуу жупташтыруу"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Түзмөктү QR код аркылуу жупташтыруу"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодунун сканерин колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Түзмөктү атайын код аркылуу жупташтыруу"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Жаңы түзмөктөрдү алты сандан турган код аркылуу жупташтырасыз"</string> @@ -246,12 +246,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi аркылуу байланыштыруу коду"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Туташкан жок"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Түзмөк бир тармакка туташып турушу керек."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодду скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Түзмөк жупташтырылууда…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Түзмөк жупташтырылган жок. QR коду туура эмес же түзмөк бир тармакка туташпай турат."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Түзмөк жупташтырылган жок. QR код туура эмес же түзмөк бир тармакка туташпай турат."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP дарек жана порт"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодун скандоо"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодду скандоо"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодду скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi тармагына туташыңыз"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, мүчүлүштүктөрдү оңдоо, иштеп чыгуу"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Ката жөнүндө кабарлоо"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ушул телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ушул планшет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док бекети үчүн динамик"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Туташкан түзмөк"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 25466d4c91e5..28fd87e96c7d 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ແທັບເລັດນີ້"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ແທ່ນວາງລຳໂພງ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ຫຼິ້ນຢູ່ອຸປະກອນນີ້ບໍ່ໄດ້"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ອັບເກຣດບັນຊີເພື່ອສະຫຼັບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index ff9f1c294c5a..8246c6bfd168 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis telefonas"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetinis kompiuteris"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doko garsiakalbis"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Prijungtas įrenginys"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Negalima leisti šiame įrenginyje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Jei norite perjungti, naujovinkite paskyrą"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 91051539f2ad..807df3a3f91d 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis tālrunis"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetdators"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doka skaļrunis"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pievienotā ierīce"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nevar atskaņot šajā ierīcē."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lai pārslēgtu, jauniniet kontu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 0005ec5175dc..f7b4c0feed86 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овој телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овој таблет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док со звучник"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Поврзан уред"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не може да се пушти на уредов"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надградете ја сметката за да се префрлите"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 12d6e5269725..610f3653e716 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ഈ ഫോൺ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ഈ ടാബ്ലെറ്റ്"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ഡോക്ക് സ്പീക്കർ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"കണക്റ്റ് ചെയ്ത ഉപകരണം"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ഈ ഉപകരണത്തിൽ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"അക്കൗണ്ട് മാറാൻ അപ്ഗ്രേഡ് ചെയ്യുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index d2e03f2fb8a5..040f2c82c212 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Энэ утас"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Энэ таблет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Суурилуулагчийн чанга яригч"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Холбогдсон төхөөрөмж"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Энэ төхөөрөмжид тоглуулах боломжгүй"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Сэлгэхийн тулд бүртгэлийг сайжруулна уу"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 8c2b4ae4b082..932ef6c9e8dc 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"हा फोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"हा टॅबलेट"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट केलेले डिव्हाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"या डिव्हाइसवर प्ले करू शकत नाही"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"स्विच करण्यासाठी खाते अपग्रेड करा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index e991b2428404..460eb563adbc 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefon ini"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Pembesar suara dok"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Peranti yang disambungkan"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat dimainkan pada peranti ini"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Tingkatkan akaun untuk beralih"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 12bd02e553f9..8458a629db79 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ဤဖုန်း"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ဤတက်ဘလက်"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"အထိုင် စပီကာ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ချိတ်ဆက်ကိရိယာ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ဤစက်ပစ္စည်းတွင် ဖွင့်၍မရပါ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ပြောင်းရန် အကောင့်အဆင့်ကိုမြှင့်ပါ"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index b85dabb593f5..a082be68feac 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefonen"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Dette nettbrettet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhøyttaler"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tilkoblet enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke spille på denne enheten"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oppgrader kontoen for å bytte"</string> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index fccd8bbea1f2..f464f3135f49 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -23,7 +23,7 @@ <string-array name="wifi_status"> <item msgid="1596683495752107015"></item> <item msgid="3288373008277313483">"स्क्यान गरिँदै..."</item> - <item msgid="6050951078202663628">"जडान हुँदै..."</item> + <item msgid="6050951078202663628">"कनेक्ट गरिँदै छ..."</item> <item msgid="8356618438494652335">"प्रमाणित गर्दै ..."</item> <item msgid="2837871868181677206">"IP एड्रेस पत्ता लगाउँदै ..."</item> <item msgid="4613015005934755724">"जडान गरिएको"</item> @@ -37,7 +37,7 @@ <string-array name="wifi_status_with_ssid"> <item msgid="5969842512724979061"></item> <item msgid="1818677602615822316">"स्क्यान गर्दै..."</item> - <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग जडान हुँदै..."</item> + <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग कनेक्ट गरिँदै छ..."</item> <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>को साथ प्रमाणित गर्दै…"</item> <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>बाट IP एड्रेस प्राप्त गर्दै…"</item> <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> मा कनेक्ट भएको छ छ"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 59a60db9915d..4e3455256ba1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"विच्छेदन गरियो"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"जडान हटाइँदै ..."</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"जडान हुँदै..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"कनेक्ट गरिँदै छ..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> सँग कनेक्ट भएको छ"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"कनेक्ट गरिँदै छ..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"जडान गरियो (फोनबाहेेक) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"ब्याट्रीको स्तर: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"सक्रिय"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"सक्रिय, बायाँ मात्र"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"बायाँ मात्र अन छ"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"सक्रिय, दायाँ मात्र"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"सक्रिय, बायाँ र दायाँ"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"मिडिया अडियो"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यो फोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यो ट्याब्लेट"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डक स्पिकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट गरिएको डिभाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"यो डिभाइसमा मिडिया प्ले गर्न मिल्दैन"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"आफूले प्रयोग गर्न चाहेको खाता अपग्रेड गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 02cdba4b12b8..1b456b94f355 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Deze telefoon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Deze tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockspeaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbonden apparaat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan niet afspelen op dit apparaat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade het account om te schakelen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 699989b189a9..99bcafef0543 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -84,9 +84,9 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"ବିଛିନ୍ନ ହେଲା"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ବିଚ୍ଛିନ୍ନ କରୁଛି…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"ସଂଯୋଗ କରାଯାଉଛି…"</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"କନେକ୍ଟ ହେଉଛି…"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"ସଂଯୁକ୍ତ ହେଲା<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"ପେୟାର୍ କରୁଛି…"</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"ପେୟାର କରୁଛି…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ସଂଯୁକ୍ତ ହେଲା (ମିଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ କିମ୍ବା ମେଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -174,7 +174,7 @@ <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_title" msgid="7602210956640483039">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ଆଉଟ୍ପୁଟ୍"</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> <string name="tts_default_pitch_title" msgid="6988592215554485479">"ପିଚ୍"</string> @@ -250,7 +250,7 @@ <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ଡିଭାଇସ୍ ପେୟାର୍ କରାଯାଉଛି…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ଡିଭାଇସରୁ ପେୟାର୍ ହେବାରେ ବିଫଳ ହୋଇଛି। QR କୋଡ୍ ସଠିକ୍ ନଥିଲା ବା ଡିଭାଇସ୍ ସମାନ ନେଟୱାର୍କରେ ସଂଯୋଗ ହୋଇନାହିଁ।"</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP ଠିକଣା ଓ ପୋର୍ଟ"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR କୋଡ୍ ସ୍କାନ୍ କରନ୍ତୁ"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR କୋଡ ସ୍କାନ କରନ୍ତୁ"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"ଏକ QR କୋଡ୍ ସ୍କାନ୍ କରି ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଡିଭାଇସ୍ ପେୟାର୍ କରନ୍ତୁ"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"ଦୟାକରି ଏକ ୱାଇ-ଫାଇ ନେଟୱାର୍କରେ ସଂଯୋଗ କରନ୍ତୁ"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ଡିବଗ୍, dev"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ଏହି ଟାବଲେଟ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ଡକ ସ୍ପିକର"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"କନେକ୍ଟ କରାଯାଇଥିବା ଡିଭାଇସ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 482315fd785b..3e202010c85f 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ਇਹ ਟੈਬਲੈੱਟ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ਡੌਕ ਸਪੀਕਰ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ਸਵਿੱਚ ਕਰਨ ਲਈ ਖਾਤੇ ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 742574450a6f..58a89d96e5ac 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Rozłączono"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Rozłączanie..."</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Łączenie..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Łączę..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Połączono – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Paruję…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Połączono (bez telefonu) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -99,8 +99,8 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Urządzenie aktywne"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywny, tylko lewa strona"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywny, tylko prawa strona"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywne, tylko lewa strona"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywne, tylko prawa strona"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktywny, lewa i prawa strona"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Dźwięk multimediów"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Połączenia telefoniczne"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ten telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ten tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Głośnik ze stacją dokującą"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Połączone urządzenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nie można odtworzyć na tym urządzeniu"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aby przełączyć, potrzebujesz konta premium"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index e8a39664034b..678c076aa03b 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telefone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 8b9d33af0cd3..847bfb5634e7 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telemóvel"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altifalante estação carregam."</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index e8a39664034b..678c076aa03b 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telefone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 244dc6fed7d2..7fa228bc5be6 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Acest telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Această tabletă"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Difuzorul dispozitivului de andocare"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispozitiv conectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nu se poate reda pe acest dispozitiv"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Fă upgrade contului pentru a comuta"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 321fe6faa886..a53b67afc002 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Этот смартфон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Этот планшет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Колонка с док-станцией"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Подключенное устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Невозможно воспроизвести на этом устройстве."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Для переключения требуется премиум-аккаунт"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 400a6e5c44da..ab5d3ceb8705 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"මෙම දුරකථනය"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"මෙම ටැබ්ලටය"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ඩොක් ස්පීකරය"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"සම්බන්ධ කළ උපාංගය"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"මෙම උපාංගය මත ධාවනය කළ නොහැක"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"මාරු වීමට ගිණුම උත්ශ්රේණි කරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 3c2b7ba45986..daf9492b351d 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -107,7 +107,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľanie kontaktov a histórie hovorov"</string> + <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľať kontakty a históriu hovorov"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefón"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pripojené zariadenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V tomto zariadení sa nedá prehrávať obsah"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Inovujte účet a prejdite naň"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 86cb46d572c6..7164da61c5d5 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ta telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ta tablični računalnik"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvočnik nosilca"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezana naprava"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ni mogoče predvajati v tej napravi."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Za preklop je potrebna nadgradnja računa"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index c2cecebb142a..7fd6ec9ab15f 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -455,7 +455,7 @@ <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string> <string name="power_discharge_by" msgid="4113180890060388350">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Deri në: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria mund të mbarojë deri në <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> e mbetur"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> e mbetur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ky telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ky tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nuk mund të luhet në këtë pajisje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Përmirëso llogarinë për të ndryshuar"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 041b515a18ce..86675cf6f9ba 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овај телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овај таблет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Звучник базне станице"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Повезани уређај"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можете да пустите на овом уређају"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надоградите налог ради пребацивања"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 453f2cf96946..0d71afd98fa9 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Den här telefonen"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Den här surfplattan"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockningsstationens högtalare"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ansluten enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan inte spelas på denna enhet"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppgradera kontot för att byta"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index d6fdc1ebbdaf..303131e3a5f2 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Simu hii"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Kompyuta kibao hii"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Spika ya kituo"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Kifaa kilichounganishwa"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Huwezi kucheza maudhui kwenye kifaa hiki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Pata toleo jipya la akaunti ili ubadilishe"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index fa39a7179ea4..83aa2aff8a1c 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"இந்த மொபைல்"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"இந்த டேப்லெட்"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"டாக் ஸ்பீக்கர்"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"இணைக்கப்பட்டுள்ள சாதனம்"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"இந்தச் சாதனத்தில் பிளே செய்ய முடியவில்லை"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"மாற்ற, கணக்கை மேம்படுத்துங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index db2307d9b618..d250352ea811 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -66,7 +66,7 @@ <string-array name="bt_hci_snoop_log_filters_entries"> <item msgid="195768089203590086">"ACLహెడర్లను మాత్రమే వదిలివేయండి"</item> <item msgid="2776218217644557831">"A2DP మీడియా ప్యాకెట్లను ఫిల్టర్ చేయండి"</item> - <item msgid="8163235976612675092">"RFCOMM ఛానెల్ని ఫిల్టర్ చేయండి"</item> + <item msgid="8163235976612675092">"RFCOMM ఛానెల్ను ఫిల్టర్ చేయండి"</item> </string-array> <string-array name="bt_hci_snoop_log_profile_filter_entries"> <item msgid="3961868665260627524">"డిజేబుల్ చేయండి"</item> @@ -269,10 +269,10 @@ <string-array name="app_process_limit_entries"> <item msgid="794656271086646068">"స్టాండర్డ్ పరిమితి"</item> <item msgid="8628438298170567201">"నేపథ్య ప్రాసెస్లు లేవు"</item> - <item msgid="915752993383950932">"గరిష్టంగా 1 ప్రాసెస్"</item> - <item msgid="8554877790859095133">"గరిష్టంగా 2 ప్రాసెస్లు"</item> - <item msgid="9060830517215174315">"గరిష్టంగా 3 ప్రాసెస్లు"</item> - <item msgid="6506681373060736204">"గరిష్టంగా 4 ప్రాసెస్లు"</item> + <item msgid="915752993383950932">"గరిష్ఠంగా 1 ప్రాసెస్"</item> + <item msgid="8554877790859095133">"గరిష్ఠంగా 2 ప్రాసెస్లు"</item> + <item msgid="9060830517215174315">"గరిష్ఠంగా 3 ప్రాసెస్లు"</item> + <item msgid="6506681373060736204">"గరిష్ఠంగా 4 ప్రాసెస్లు"</item> </string-array> <string-array name="usb_configuration_titles"> <item msgid="3358668781763928157">"ఛార్జింగ్"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index e8336f00605f..e7cf933b05ad 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ఈ ఫోన్"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ఈ టాబ్లెట్"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"డాక్ స్పీకర్"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్టర్నల్ పరికరం"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"కనెక్ట్ చేసిన పరికరం"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ఈ పరికరంలో ప్లే చేయడం సాధ్యపడదు"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"మారడానికి ఖాతాను అప్గ్రేడ్ చేయండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 8335cbafd0af..32629c32f969 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"แท็บเล็ตเครื่องนี้"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"แท่นวางลำโพง"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"อุปกรณ์ที่เชื่อมต่อ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"เล่นในอุปกรณ์นี้ไม่ได้"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"อัปเกรดบัญชีเพื่อเปลี่ยน"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index dd729de51183..e60faf6bc823 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ang teleponong ito"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ang tablet na ito"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker ng dock"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Nakakonektang device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Hindi ma-play sa device na ito"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"I-upgrade ang account para lumipat"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index a2417ed5f036..3ea12adfd3df 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu tablet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Yuva hoparlörü"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Bağlı cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oynatılamıyor"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Geçiş yapmak için hesabı yükseltin"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index b5c31f4ec516..95ece2660b76 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -107,7 +107,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ до контактів та історії дзвінків"</string> + <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ до контактів і історії викликів"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Використовуйте, щоб надсилати контакти й історію викликів"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string> @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Цей телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Цей планшет"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамік док-станції"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Підключений пристрій"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можна відтворювати тут"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Потрібний платний обліковий запис"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index fea22aa5dc96..70aa9a3ddbb7 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"یہ فون"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"یہ ٹیبلیٹ"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ڈاک اسپیکر"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"منسلک آلہ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"اس آلے پر چلایا نہیں جا سکتا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"سوئچ کرنے کے لیے اکاؤنٹ اپ گریڈ کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 312a22e12d68..5f42969a23f1 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Shu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Shu planshet"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok-stansiyali karnay"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ulangan qurilma"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu qurilmada ijro etilmaydi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oʻtish uchun hisobingizni yangilang"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index b16f200155d5..d75f1b266283 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Điện thoại này"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Máy tính bảng này"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Loa có gắn đế"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Thiết bị đã kết nối"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Không phát được trên thiết bị này"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nâng cấp tài khoản để chuyển đổi"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index d266d142e904..23b6ff3052af 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"这部手机"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"这台平板电脑"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"无法在此设备上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"升级帐号后才能切换"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index be7c044f4a2e..e6da4bc6eca5 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"此手機"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"此平板電腦"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連接的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在此裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 59105e5ac0ba..e33934cead42 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"這支手機"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"這台平板電腦"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"座架喇叭"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連結的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在這部裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 87cf1f7e58a9..5c7a7b557f64 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -545,12 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Le foni"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Le thebulethi"</string> - <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> - <skip /> - <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> - <skip /> - <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> - <skip /> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Isipikha sentuba"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Idivayisi exhunyiwe"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ayikwazi ukudlala kule divayisi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Thuthukisa i-akhawunti ukuze ushintshe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index dac7f8d15388..b44c0e0799b2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -929,7 +929,7 @@ <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=100] --> <string name="show_all_anrs_summary">Display App Not Responding dialog for background apps</string> - <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=25] --> + <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=30] --> <string name="show_notification_channel_warnings">Show notification channel warnings</string> <!-- UI debug setting: show missing channel toasts summary [CHAR LIMIT=50] --> <string name="show_notification_channel_warnings_summary">Displays on-screen warning when an app posts a notification without a valid channel</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 3fcb7f398185..ffc0479f01c3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -116,10 +116,7 @@ public class InfoMediaManager extends MediaManager { && !TextUtils.isEmpty(mPackageName)) { RouteListingPreference routeListingPreference = mRouterManager.getRouteListingPreference(mPackageName); - if (routeListingPreference != null) { - Api34Impl.onRouteListingPreferenceUpdated(null, routeListingPreference, - mPreferenceItemMap); - } + Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap); } refreshDevices(); } @@ -631,7 +628,7 @@ public class InfoMediaManager extends MediaManager { } /** - * Ignore callback here since we'll also receive {@link onRequestFailed} with reason code. + * Ignore callback here since we'll also receive {@link #onRequestFailed} with reason code. */ @Override public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) { @@ -656,9 +653,9 @@ public class InfoMediaManager extends MediaManager { public void onRouteListingPreferenceUpdated( String packageName, RouteListingPreference routeListingPreference) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - Api34Impl.onRouteListingPreferenceUpdated(packageName, routeListingPreference, - mPreferenceItemMap); + if (TextUtils.equals(mPackageName, packageName)) { + Api34Impl.onRouteListingPreferenceUpdated( + routeListingPreference, mPreferenceItemMap); refreshDevices(); } } @@ -746,7 +743,6 @@ public class InfoMediaManager extends MediaManager { @DoNotInline static void onRouteListingPreferenceUpdated( - String packageName, RouteListingPreference routeListingPreference, Map<String, RouteListingPreference.Item> preferenceItemMap) { preferenceItemMap.clear(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index aa5f3df1b750..39780f3a96ed 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -73,6 +73,7 @@ import java.util.Set; public class InfoMediaManagerTest { private static final String TEST_PACKAGE_NAME = "com.test.packagename"; + private static final String TEST_PACKAGE_NAME_2 = "com.test.packagename2"; private static final String TEST_ID = "test_id"; private static final String TEST_ID_1 = "test_id_1"; private static final String TEST_ID_2 = "test_id_2"; @@ -308,7 +309,54 @@ public class InfoMediaManagerTest { } @Test - public void onRouteChanged_getAvailableRoutesWithPrefernceListExit_ordersRoutes() { + public void onRouteChanged_getAvailableRoutesWithPreferenceListExit_ordersRoutes() { + RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME); + setUpSelectedRoutes(TEST_PACKAGE_NAME); + + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo); + + when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); + when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); + + setAvailableRoutesList(TEST_PACKAGE_NAME); + + mInfoMediaManager.mRouterManager = mRouterManager; + mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME, + routeListingPreference); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); + + assertThat(mInfoMediaManager.mMediaDevices).hasSize(3); + assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); + assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4); + assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue(); + assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3); + } + + @Test + public void onRouteChanged_preferenceListUpdateWithDifferentPkg_notOrdersRoutes() { + RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME_2); + setUpSelectedRoutes(TEST_PACKAGE_NAME); + + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo); + + when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); + when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); + + setAvailableRoutesList(TEST_PACKAGE_NAME); + mInfoMediaManager.mRouterManager = mRouterManager; + mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME_2, + routeListingPreference); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); + + assertThat(mInfoMediaManager.mMediaDevices).hasSize(1); + assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); + } + + private RouteListingPreference setUpPreferenceList(String packageName) { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.UPSIDE_DOWN_CAKE); final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>(); @@ -324,57 +372,40 @@ public class InfoMediaManagerTest { RouteListingPreference routeListingPreference = new RouteListingPreference.Builder().setItems( preferenceItemList).setUseSystemOrdering(false).build(); - when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME)) + when(mRouterManager.getRouteListingPreference(packageName)) .thenReturn(routeListingPreference); + return routeListingPreference; + } + private void setUpSelectedRoutes(String packageName) { final List<MediaRoute2Info> selectedRoutes = new ArrayList<>(); final MediaRoute2Info info = mock(MediaRoute2Info.class); when(info.getId()).thenReturn(TEST_ID); - when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(info.getClientPackageName()).thenReturn(packageName); when(info.isSystemRoute()).thenReturn(true); selectedRoutes.add(info); when(mRouterManager.getSelectedRoutes(any())).thenReturn(selectedRoutes); - - final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); - final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); - routingSessionInfos.add(sessionInfo); - - when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); - when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); - - setAvailableRoutesList(); - - mInfoMediaManager.mRouterManager = mRouterManager; - mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME, - routeListingPreference); - mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); - - assertThat(mInfoMediaManager.mMediaDevices).hasSize(3); - assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); - assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4); - assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue(); - assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3); } - private List<MediaRoute2Info> setAvailableRoutesList() { + private List<MediaRoute2Info> setAvailableRoutesList(String packageName) { final List<MediaRoute2Info> availableRoutes = new ArrayList<>(); final MediaRoute2Info availableInfo1 = mock(MediaRoute2Info.class); when(availableInfo1.getId()).thenReturn(TEST_ID_2); - when(availableInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo1.getClientPackageName()).thenReturn(packageName); when(availableInfo1.getType()).thenReturn(TYPE_REMOTE_TV); availableRoutes.add(availableInfo1); final MediaRoute2Info availableInfo2 = mock(MediaRoute2Info.class); when(availableInfo2.getId()).thenReturn(TEST_ID_3); - when(availableInfo2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo2.getClientPackageName()).thenReturn(packageName); availableRoutes.add(availableInfo2); final MediaRoute2Info availableInfo3 = mock(MediaRoute2Info.class); when(availableInfo3.getId()).thenReturn(TEST_ID_4); - when(availableInfo3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo3.getClientPackageName()).thenReturn(packageName); availableRoutes.add(availableInfo3); - when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn( + when(mRouterManager.getAvailableRoutes(packageName)).thenReturn( availableRoutes); return availableRoutes; diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fe90caf2646c..0aa121dfcc49 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -913,8 +913,9 @@ android:excludeFromRecents="true" android:launchMode="singleInstance" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:visibleToInstantApps="true"> - </activity> + android:visibleToInstantApps="true" + android:exported="true" + /> <activity android:name=".controls.management.ControlsEditingActivity" android:label="@string/controls_menu_edit" @@ -947,8 +948,9 @@ android:finishOnTaskLaunch="true" android:launchMode="singleInstance" android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|orientation" - android:visibleToInstantApps="true"> - </activity> + android:visibleToInstantApps="true" + android:exported="true" + /> <activity android:name=".wallet.ui.WalletActivity" android:label="@string/wallet_title" @@ -994,7 +996,6 @@ android:name=".notetask.shortcut.LaunchNoteTaskActivity" android:exported="true" android:excludeFromRecents="true" - android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" > <intent-filter> @@ -1010,7 +1011,6 @@ android:exported="false" android:enabled="true" android:excludeFromRecents="true" - android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" /> <activity diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml index fb31e1da6361..9d9b05a5e3cb 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menü für Bedienungshilfen"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistant"</string> <string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Einstellungen für Bedienungshilfen"</string> @@ -20,9 +20,9 @@ <string name="brightness_down_label" msgid="7115662941913272072">"Helligkeit verringern"</string> <string name="previous_button_content_description" msgid="840869171117765966">"Zum vorherigen Bildschirm"</string> <string name="next_button_content_description" msgid="6810058269847364406">"Zum nächsten Bildschirm"</string> - <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="accessibility_menu_summary" msgid="340071398148208130">"Gerät mit großem Menü steuern"</string> - <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Menüeinstellungen f. Bedienungshilfen"</string> + <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Einstellungen für das Menü „Bedienungshilfen“"</string> <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Große Schaltflächen"</string> <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Schaltflächen für das Menü „Bedienungshilfen“ vergrößern"</string> <string name="pref_help_title" msgid="6871558837025010641">"Hilfe"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml index 9726f2056a36..48c2634191d9 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml @@ -11,7 +11,7 @@ <string name="recent_apps_label" msgid="6583276995616385847">"Соңғы қолданбалар"</string> <string name="lockscreen_label" msgid="648347953557887087">"Құлып экраны"</string> <string name="quick_settings_label" msgid="2999117381487601865">"Жылдам параметрлер"</string> - <string name="notifications_label" msgid="6829741046963013567">"Хабарланды00ADрулар"</string> + <string name="notifications_label" msgid="6829741046963013567">"Хабарландырулар"</string> <string name="screenshot_label" msgid="863978141223970162">"Скриншот"</string> <string name="screenshot_utterance" msgid="1430760563401895074">"Скриншот жасау"</string> <string name="volume_up_label" msgid="8592766918780362870">"Дыбысын арттыру"</string> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt index 7e1bfb921ca9..addabcc0282a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt @@ -21,7 +21,6 @@ import android.graphics.fonts.FontVariationAxis import android.util.Log import android.util.LruCache import android.util.MathUtils -import android.util.MathUtils.abs import androidx.annotation.VisibleForTesting import java.lang.Float.max import java.lang.Float.min @@ -30,8 +29,6 @@ private const val TAG_WGHT = "wght" private const val TAG_ITAL = "ital" private const val FONT_WEIGHT_DEFAULT_VALUE = 400f -private const val FONT_WEIGHT_ANIMATION_FRAME_COUNT = 100 - private const val FONT_ITALIC_MAX = 1f private const val FONT_ITALIC_MIN = 0f private const val FONT_ITALIC_ANIMATION_STEP = 0.1f @@ -39,11 +36,12 @@ private const val FONT_ITALIC_DEFAULT_VALUE = 0f // Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in // frame draw time on a Pixel 6. -@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10 +@VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10 /** Provide interpolation of two fonts by adjusting font variation settings. */ -class FontInterpolator { - +class FontInterpolator( + numberOfAnimationSteps: Int? = null, +) { /** * Cache key for the interpolated font. * @@ -88,8 +86,9 @@ class FontInterpolator { // Font interpolator has two level caches: one for input and one for font with different // variation settings. No synchronization is needed since FontInterpolator is not designed to be // thread-safe and can be used only on UI thread. - private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES) - private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES) + val cacheMaxEntries = numberOfAnimationSteps?.let { it * 2 } ?: DEFAULT_FONT_CACHE_MAX_ENTRIES + private val interpCache = LruCache<InterpKey, Font>(cacheMaxEntries) + private val verFontCache = LruCache<VarFontKey, Font>(cacheMaxEntries) // Mutable keys for recycling. private val tmpInterpKey = InterpKey(null, null, 0f) @@ -128,18 +127,12 @@ class FontInterpolator { val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> when (tag) { - // TODO: Good to parse 'fvar' table for retrieving default value. - TAG_WGHT -> { - adaptiveAdjustWeight( - MathUtils.lerp( - startValue ?: FONT_WEIGHT_DEFAULT_VALUE, - endValue ?: FONT_WEIGHT_DEFAULT_VALUE, - progress - ), + TAG_WGHT -> + MathUtils.lerp( startValue ?: FONT_WEIGHT_DEFAULT_VALUE, endValue ?: FONT_WEIGHT_DEFAULT_VALUE, + progress ) - } TAG_ITAL -> adjustItalic( MathUtils.lerp( @@ -175,9 +168,9 @@ class FontInterpolator { val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build() interpCache.put(InterpKey(start, end, progress), newFont) verFontCache.put(VarFontKey(start, newAxes), newFont) - if (DEBUG) { - Log.d(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey") - } + + // Cache misses are likely to create memory leaks, so this is logged at error level. + Log.e(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey") return newFont } @@ -225,15 +218,6 @@ class FontInterpolator { return result } - // For the performance reasons, we animate weight with adaptive step. This helps - // Cache hit ratio in the Skia glyph cache. - // The reason we don't use fix step is because the range of weight axis is not normalized, - // some are from 50 to 100, others are from 0 to 1000, so we cannot give a constant proper step - private fun adaptiveAdjustWeight(value: Float, start: Float, end: Float): Float { - val step = max(abs(end - start) / FONT_WEIGHT_ANIMATION_FRAME_COUNT, 1F) - return coerceInWithStep(value, min(start, end), max(start, end), step) - } - // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps // Cache hit ratio in the Skia glyph cache. private fun adjustItalic(value: Float) = diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 16ddf0c36d9d..b555fa583588 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -108,7 +108,8 @@ class TextAnimator( } // Following two members are for mutable for testing purposes. - public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache) + public var textInterpolator: TextInterpolator = + TextInterpolator(layout, typefaceCache, numberOfAnimationSteps) public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 8ed8d8fb61fd..02caeeddd774 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -31,6 +31,7 @@ import java.lang.Math.max class TextInterpolator( layout: Layout, var typefaceCache: TypefaceVariantCache, + numberOfAnimationSteps: Int? = null, ) { /** * Returns base paint used for interpolation. @@ -85,7 +86,7 @@ class TextInterpolator( private class Line(val runs: List<Run>) private var lines = listOf<Line>() - private val fontInterpolator = FontInterpolator() + private val fontInterpolator = FontInterpolator(numberOfAnimationSteps) // Recycling object for glyph drawing and tweaking. private val tmpPaint = TextPaint() diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 89871fa7d875..2cd587ffbc45 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -56,7 +56,19 @@ data class TurbulenceNoiseAnimationConfig( val easeOutDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val pixelDensity: Float = 1f, val blendMode: BlendMode = DEFAULT_BLEND_MODE, - val onAnimationEnd: Runnable? = null + val onAnimationEnd: Runnable? = null, + /** + * Variants in noise. Higher number means more contrast; lower number means less contrast but + * make the noise dimmed. You may want to increase the [lumaMatteBlendFactor] to compensate. + * Expected range [0, 1]. + */ + val lumaMatteBlendFactor: Float = DEFAULT_LUMA_MATTE_BLEND_FACTOR, + /** + * Offset for the overall brightness in noise. Higher number makes the noise brighter. You may + * want to use this if you have made the noise softer using [lumaMatteBlendFactor]. Expected + * range [0, 1]. + */ + val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS ) { companion object { const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec @@ -66,6 +78,8 @@ data class TurbulenceNoiseAnimationConfig( const val DEFAULT_NOISE_SPEED_Z = 0.3f const val DEFAULT_OPACITY = 150 // full opacity is 255. const val DEFAULT_COLOR = Color.WHITE + const val DEFAULT_LUMA_MATTE_BLEND_FACTOR = 1f + const val DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS = 0f const val DEFAULT_BACKGROUND_COLOR = Color.BLACK val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt index d1ba7c4de35c..d3c57c91405a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt @@ -37,6 +37,8 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uniform float in_opacity; uniform float in_pixelDensity; uniform float in_inverseLuma; + uniform half in_lumaMatteBlendFactor; + uniform half in_lumaMatteOverallBrightness; layout(color) uniform vec4 in_color; layout(color) uniform vec4 in_backgroundColor; """ @@ -48,18 +50,21 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = abs(in_inverseLuma - simplex3d(noiseP)) * in_opacity; + // Bring it to [0, 1] range. + float luma = (simplex3d(noiseP) * in_inverseLuma) * 0.5 + 0.5; + luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) + * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; - // Add dither with triangle distribution to avoid color banding. Ok to dither in the + // Add dither with triangle distribution to avoid color banding. Dither in the // shader here as we are in gamma space. float dither = triangleNoise(p * in_pixelDensity) / 255.; // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to // multiply rgb with a to get the correct result. - color = (color + dither.rrr) * in_color.a; - return vec4(color, in_color.a); + color = (color + dither.rrr) * in_opacity; + return vec4(color, in_opacity); } """ @@ -70,12 +75,15 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = abs(in_inverseLuma - simplex3d_fractal(noiseP)) * in_opacity; + // Bring it to [0, 1] range. + float luma = (simplex3d_fractal(noiseP) * in_inverseLuma) * 0.5 + 0.5; + luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) + * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; // Skip dithering. - return vec4(color * in_color.a, in_color.a); + return vec4(color * in_opacity, in_opacity); } """ @@ -125,6 +133,28 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : } /** + * Sets blend and brightness factors of the luma matte. + * + * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting + * this a lower number removes variations. I.e. the turbulence noise will look more blended. + * Expected input range is [0, 1]. more dimmed. + * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise. + * Expected input range is [0, 1]. + * + * Example usage: You may want to apply a small number to [lumaMatteBlendFactor], such as 0.2, + * which makes the noise look softer. However it makes the overall noise look dim, so you want + * offset something like 0.3 for [lumaMatteOverallBrightness] to bring back its overall + * brightness. + */ + fun setLumaMatteFactors( + lumaMatteBlendFactor: Float = 1f, + lumaMatteOverallBrightness: Float = 0f + ) { + setFloatUniform("in_lumaMatteBlendFactor", lumaMatteBlendFactor) + setFloatUniform("in_lumaMatteOverallBrightness", lumaMatteOverallBrightness) + } + + /** * Sets whether to inverse the luminosity of the noise. * * By default noise will be used as a luma matte as is. This means that you will see color in @@ -132,7 +162,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : * true. */ fun setInverseNoiseLuminosity(inverse: Boolean) { - setFloatUniform("in_inverseLuma", if (inverse) 1f else 0f) + setFloatUniform("in_inverseLuma", if (inverse) -1f else 1f) } /** Current noise movements in x, y, and z axes. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt index c3e84787d4fb..43d6504fce84 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt @@ -215,10 +215,12 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex noiseConfig = config with(turbulenceNoiseShader) { setGridCount(config.gridCount) - setColor(ColorUtils.setAlphaComponent(config.color, config.opacity)) + setColor(config.color) setBackgroundColor(config.backgroundColor) setSize(config.width, config.height) setPixelDensity(config.pixelDensity) + setInverseNoiseLuminosity(inverse = false) + setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness) } paint.blendMode = config.blendMode } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 0e20444347fd..8cba2ab0b70b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -48,6 +48,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext private val KEY_TIMESTAMP = "appliedTimestamp" +private val KNOWN_PLUGINS = + mapOf<String, List<ClockMetadata>>( + "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")), + "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")), + "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")), + "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")), + "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")), + "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")), + "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")), + "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")), + "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")), + ) private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut( key: TKey, @@ -127,8 +139,61 @@ open class ClockRegistry( private val pluginListener = object : PluginListener<ClockProviderPlugin> { - override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) { - manager.loadPlugin() + override fun onPluginAttached( + manager: PluginLifecycleManager<ClockProviderPlugin> + ): Boolean { + if (keepAllLoaded) { + // Always load new plugins if requested + return true + } + + val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) + if (knownClocks == null) { + logBuffer.tryLog( + TAG, + LogLevel.WARNING, + { str1 = manager.getPackage() }, + { "Loading unrecognized clock package: $str1" } + ) + return true + } + + logBuffer.tryLog( + TAG, + LogLevel.INFO, + { str1 = manager.getPackage() }, + { "Skipping initial load of known clock package package: $str1" } + ) + + var isClockListChanged = false + for (metadata in knownClocks) { + val id = metadata.clockId + val info = + availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) { + isClockListChanged = true + onConnected(id) + } + + if (manager != info.manager) { + logBuffer.tryLog( + TAG, + LogLevel.ERROR, + { str1 = id }, + { "Clock Id conflict on known attach: $str1 is double registered" } + ) + continue + } + + info.provider = null + } + + if (isClockListChanged) { + triggerOnAvailableClocksChanged() + } + verifyLoadedProviders() + + // Load executed via verifyLoadedProviders + return false } override fun onPluginLoaded( 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 cd9fb886a4e6..e0506046ee54 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 @@ -148,11 +148,11 @@ interface CustomizationProviderClient { */ val isEnabled: Boolean = true, /** - * If the affordance is disabled, this is a set of instruction messages to be shown to the - * user when the disabled affordance is selected. The instructions should help the user - * figure out what to do in order to re-neable this affordance. + * If the affordance is disabled, this is the explanation to be shown to the user when the + * disabled affordance is selected. The instructions should help the user figure out what to + * do in order to re-neable this affordance. */ - val enablementInstructions: List<String>? = null, + val enablementExplanation: String? = null, /** * If the affordance is disabled, this is a label for a button shown together with the set * of instruction messages when the disabled affordance is selected. The button should help @@ -163,15 +163,14 @@ interface CustomizationProviderClient { */ val enablementActionText: String? = null, /** - * If the affordance is disabled, this is a "component name" of the format - * `packageName/action` to be used as an `Intent` for `startActivity` when the action button - * (shown together with the set of instruction messages when the disabled affordance is - * selected) is clicked by the user. The button should help send the user to a flow that - * would help them achieve the instructions and re-enable this affordance. + * If the affordance is disabled, this is an [Intent] to be used with `startActivity` when + * the action button (shown together with the set of instruction messages when the disabled + * affordance is selected) is clicked by the user. The button should help send the user to a + * flow that would help them achieve the instructions and re-enable this affordance. * * If `null`, the button should not be shown. */ - val enablementActionComponentName: String? = null, + val enablementActionIntent: Intent? = null, /** Optional [Intent] to use to start an activity to configure this affordance. */ val configureIntent: Intent? = null, ) @@ -327,20 +326,20 @@ class CustomizationProviderClientImpl( Contract.LockScreenQuickAffordances.AffordanceTable.Columns .IS_ENABLED ) - val enablementInstructionsColumnIndex = + val enablementExplanationColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_INSTRUCTIONS + .ENABLEMENT_EXPLANATION ) val enablementActionTextColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns .ENABLEMENT_ACTION_TEXT ) - val enablementComponentNameColumnIndex = + val enablementActionIntentColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_COMPONENT_NAME + .ENABLEMENT_ACTION_INTENT ) val configureIntentColumnIndex = cursor.getColumnIndex( @@ -352,9 +351,9 @@ class CustomizationProviderClientImpl( nameColumnIndex == -1 || iconColumnIndex == -1 || isEnabledColumnIndex == -1 || - enablementInstructionsColumnIndex == -1 || + enablementExplanationColumnIndex == -1 || enablementActionTextColumnIndex == -1 || - enablementComponentNameColumnIndex == -1 || + enablementActionIntentColumnIndex == -1 || configureIntentColumnIndex == -1 ) { return@buildList @@ -368,21 +367,22 @@ class CustomizationProviderClientImpl( name = cursor.getString(nameColumnIndex), iconResourceId = cursor.getInt(iconColumnIndex), isEnabled = cursor.getInt(isEnabledColumnIndex) == 1, - enablementInstructions = - cursor - .getString(enablementInstructionsColumnIndex) - ?.split( - Contract.LockScreenQuickAffordances.AffordanceTable - .ENABLEMENT_INSTRUCTIONS_DELIMITER - ), + enablementExplanation = + cursor.getString(enablementExplanationColumnIndex), enablementActionText = cursor.getString(enablementActionTextColumnIndex), - enablementActionComponentName = - cursor.getString(enablementComponentNameColumnIndex), + enablementActionIntent = + cursor + .getString(enablementActionIntentColumnIndex) + ?.toIntent( + affordanceId = affordanceId, + ), configureIntent = cursor .getString(configureIntentColumnIndex) - ?.toIntent(affordanceId = affordanceId), + ?.toIntent( + affordanceId = affordanceId, + ), ) ) } @@ -524,7 +524,7 @@ class CustomizationProviderClientImpl( affordanceId: String, ): Intent? { return try { - Intent.parseUri(this, 0) + Intent.parseUri(this, Intent.URI_INTENT_SCHEME) } catch (e: URISyntaxException) { Log.w(TAG, "Cannot parse Uri into Intent for affordance with ID \"$affordanceId\"!") null diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt index f9e8aafcfa47..b6d5ef3720f5 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt @@ -81,8 +81,6 @@ object CustomizationProviderContract { const val TABLE_NAME = "affordances" val URI: Uri = LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build() - const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "][" - const val COMPONENT_NAME_SEPARATOR = "/" object Columns { /** String. Unique ID for this affordance. */ @@ -97,22 +95,21 @@ object CustomizationProviderContract { /** Integer. `1` if the affordance is enabled or `0` if it disabled. */ const val IS_ENABLED = "is_enabled" /** - * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be - * shown to the user if the affordance is disabled and the user selects the - * affordance. + * String. Text to be shown to the user if the affordance is disabled and the user + * selects the affordance. */ - const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions" + const val ENABLEMENT_EXPLANATION = "enablement_explanation" /** * String. Optional label for a button that, when clicked, opens a destination * activity where the user can re-enable the disabled affordance. */ const val ENABLEMENT_ACTION_TEXT = "enablement_action_text" /** - * String. Optional package name and activity action string, delimited by - * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that - * opens a destination where the user can re-enable the disabled affordance. + * String. Optional URI-formatted `Intent` (formatted using + * `Intent#toUri(Intent.URI_INTENT_SCHEME)` used to start an activity that opens a + * destination where the user can re-enable the disabled affordance. */ - const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent" + const val ENABLEMENT_ACTION_INTENT = "enablement_action_intent" /** * Byte array. Optional parcelled `Intent` to use to start an activity that can be * used to configure the affordance. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index 3ae328e67e3d..537b7a41a898 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -190,7 +190,9 @@ enum class ClockTickRate(val value: Int) { data class ClockMetadata( val clockId: ClockId, val name: String, -) +) { + constructor(clockId: ClockId) : this(clockId, clockId) {} +} /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java index cc6a46fa7d6b..56c3f93d49b4 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java @@ -16,12 +16,20 @@ package com.android.systemui.plugins; +import android.content.ComponentName; + /** * Provides the ability for consumers to control plugin lifecycle. * * @param <T> is the target plugin type */ public interface PluginLifecycleManager<T extends Plugin> { + /** Returns the ComponentName of the target plugin. Maybe be called when not loaded. */ + ComponentName getComponentName(); + + /** Returns the package name of the target plugin. May be called when not loaded. */ + String getPackage(); + /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java index c5f503216101..bd0bd8942d5e 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java @@ -60,13 +60,18 @@ public interface PluginListener<T extends Plugin> { /** * Called when the plugin is first attached to the host application. {@link #onPluginLoaded} - * will be automatically called as well when first attached. This may be called multiple times - * if multiple plugins are allowed. It may also be called in the future if the plugin package - * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new - * or different {@link PluginLifecycleManager}. + * will be automatically called as well when first attached if true is returned. This may be + * called multiple times if multiple plugins are allowed. It may also be called in the future + * if the plugin package changes and needs to be reloaded. Each call to + * {@link #onPluginAttached} will provide a new or different {@link PluginLifecycleManager}. + * + * @return returning true will immediately load the plugin and call onPluginLoaded with the + * created object. false will skip loading, but the listener can load it at any time using the + * provided PluginLifecycleManager. Loading plugins immediately is the default behavior. */ - default void onPluginAttached(PluginLifecycleManager<T> manager) { + default boolean onPluginAttached(PluginLifecycleManager<T> manager) { // Optional + return true; } /** diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3fc0965f4a81..fc9c917c152b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -43,7 +43,7 @@ <!-- Not quite optimal but needed to translate these items as a group. The NotificationIconContainer has its own logic for translation. --> - <LinearLayout + <com.android.keyguard.KeyguardStatusAreaView android:id="@+id/keyguard_status_area" android:orientation="vertical" android:layout_width="match_parent" @@ -63,5 +63,5 @@ android:paddingStart="@dimen/below_clock_padding_start_icons" android:visibility="invisible" /> - </LinearLayout> + </com.android.keyguard.KeyguardStatusAreaView> </com.android.keyguard.KeyguardClockSwitch> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml index 2a8092010a37..3d1cb5ee6177 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml +++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml @@ -16,5 +16,5 @@ --> <resources> <!-- Invisibility to use for the date & weather view when it is disabled by a clock --> - <integer name="keyguard_date_weather_view_invisibility">8</integer> + <integer name="keyguard_date_weather_view_invisibility">4</integer> </resources> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index e8c7ef9c33cc..4df30dc3be36 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -117,7 +117,7 @@ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ใช้รหัสผ่านแทนเพื่อเพิ่มความปลอดภัย"</string> <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ผู้ดูแลระบบล็อกอุปกรณ์"</string> <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"มีการล็อกอุปกรณ์ด้วยตัวเอง"</string> - <string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จัก"</string> + <string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จักลายนิ้วมือ"</string> <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"เปิดการเข้าถึงกล้องในการตั้งค่าเพื่อใช้การปลดล็อกด้วยใบหน้า"</string> <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ป้อน PIN ของซิม คุณพยายามได้อีก # ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์}other{ป้อน PIN ของซิม คุณลองได้อีก # ครั้ง}}"</string> <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}other{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}}"</string> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 4b7968953420..39dd90e5ed60 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -148,4 +148,9 @@ <dimen name="default_dot_diameter">34dp</dimen> <dimen name="default_dot_spacing">0dp</dimen> + <!-- Weather clock smartspace scaling to apply for the weather clock --> + <item name="weather_clock_smartspace_scale" type="dimen" format="float">1.0</item> + <dimen name="weather_clock_smartspace_translateX">0dp</dimen> + <dimen name="weather_clock_smartspace_translateY">0dp</dimen> + </resources> diff --git a/packages/SystemUI/res-keyguard/values/ids.xml b/packages/SystemUI/res-keyguard/values/ids.xml index 0dff4ffa3866..1435907eaef6 100644 --- a/packages/SystemUI/res-keyguard/values/ids.xml +++ b/packages/SystemUI/res-keyguard/values/ids.xml @@ -17,4 +17,18 @@ <resources> <item type="id" name="header_footer_views_added_tag_key" /> + + <!-- animation channels for keyguard status area --> + <item type="id" name="translate_x_clock_design_animator_tag" /> + <item type="id" name="translate_x_clock_design_animator_start_tag" /> + <item type="id" name="translate_x_clock_design_animator_end_tag" /> + <item type="id" name="translate_x_aod_animator_tag" /> + <item type="id" name="translate_x_aod_animator_start_tag" /> + <item type="id" name="translate_x_aod_animator_end_tag" /> + <item type="id" name="translate_y_clock_size_animator_tag" /> + <item type="id" name="translate_y_clock_size_animator_start_tag" /> + <item type="id" name="translate_y_clock_size_animator_end_tag" /> + <item type="id" name="translate_y_clock_design_animator_tag" /> + <item type="id" name="translate_y_clock_design_animator_start_tag" /> + <item type="id" name="translate_y_clock_design_animator_end_tag" /> </resources> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 7cf93eb185fd..cd56b8f1135a 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gesig is gestaaf"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bevestig"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tik op Bevestig om te voltooi"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Ontsluit met gesig."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ontsluit met gesig. Druk om voort te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gesig is herken. Druk om voort te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Gesig is herken. Druk die ontsluitikoon om voort te gaan."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan nie stoor nie. Probeer weer."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan nie stoor nie."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 karakters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder as 16 karakters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string> <string name="basic_status" msgid="2315371112182658176">"Maak gesprek oop"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index af07b4a65c26..e754e25f1e38 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"መልክ ተረጋግጧል"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ተረጋግጧል"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ለማጠናቀቅ አረጋግጥን መታ ያድርጉ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"በመልክ ተከፍቷል።"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"በመልክ ተከፍቷል። ለመቀጠል ይጫኑ።"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"መልክ ተለይቶ ታውቋል። ለመቀጠል ይጫኑ።"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"መልክ ተለይቶ ታውቋል። ለመቀጠል የመክፈቻ አዶውን ይጫኑ።"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ማስቀመጥ አልተቻለም። እንደገና ይሞክሩ።"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ማስቀመጥ አልተቻለም።"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ቢያንስ 4 ቁምፊዎችን ይጠቀሙ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ከ16 የሚያንሱ ቁምፊዎችን ይጠቀሙ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string> <string name="basic_status" msgid="2315371112182658176">"ውይይት ይክፈቱ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 6584363dc945..d378aa8a8913 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"تمّت مصادقة الوجه."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تمّ التأكيد."</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"يمكنك النقر على \"تأكيد\" لإكمال المهمة."</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"تم فتح قفل جهازك عند تقريبه من وجهك."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط للمتابعة."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"تم التعرّف على الوجه. اضغط للمتابعة."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"تم التعرّف على الوجه. للمتابعة، اضغط على رمز فتح القفل."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"لا يمكن إجراء الحفظ. يُرجى إعادة المحاولة."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"لا يمكن إجراء الحفظ."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"يجب استخدام 4 أحرف على الأقل."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"يجب أن يحتوي الرمز على أقل من 16 حرفًا."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string> <string name="basic_status" msgid="2315371112182658176">"محادثة مفتوحة"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 1c51918953ae..5f176ae64ab8 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"নিশ্চিত কৰিলে"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"সম্পূৰ্ণ কৰিবলৈ নিশ্চিত কৰক-ত টিপক"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা।"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। অব্যাহত ৰাখিবলৈ টিপক।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"মুখাৱয়ব চিনাক্ত কৰা হৈছে। অব্যাহত ৰাখিবলৈ টিপক।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"মুখাৱয়ব চিনাক্ত কৰা হৈছে। অব্যাহত ৰাখিবলৈ আনলক কৰক চিহ্নটোত টিপক।"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ছেভ কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ছেভ কৰিব নোৱাৰি।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"অতি কমেও ৪ টা বৰ্ণ ব্যৱহাৰ কৰক"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"১৬ টাতকৈ কম বৰ্ণ ব্যৱহাৰ কৰক"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string> <string name="basic_status" msgid="2315371112182658176">"বাৰ্তালাপ খোলক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 74b4382ae815..fcd433aed53f 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Üz doğrulandı"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Təsdiqləndi"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tamamlamaq üçün \"Təsdiq edin\" seçiminə toxunun"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Üz ilə kiliddən çıxarılıb."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Üz ilə kiliddən çıxarılıb. Davam etmək üçün basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Üz tanınıb. Davam etmək üçün basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Üz tanınıb. \"Kiliddən çıxar\" ikonasına basıb davam edin."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Yadda saxlamaq mümkün deyil. Yenə cəhd edin."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Yadda saxlamaq mümkün deyil."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Ən azı 4 simvoldan istifadə edin"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Maksimum 16 simvoldan istifadə edin"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Montaj nömrəsi"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string> <string name="basic_status" msgid="2315371112182658176">"Açıq söhbət"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 7d87c87eb4f6..d37c709c9cee 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je potvrđeno"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi da biste završili"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Otključano je licem."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano je licem. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice je prepoznato. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Lice prepoznato. Pritisnite ikonu otključavanja za nastavak."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Čuvanje nije uspelo. Probajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Čuvanje nije uspelo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite bar 4 znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string> <string name="basic_status" msgid="2315371112182658176">"Otvorite konverzaciju"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 1576fb56584d..518c5e662a21 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Твар распазнаны"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Пацверджана"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Націсніце \"Пацвердзіць\", каб завяршыць"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Разблакіравана распазнаваннем твару. Націсніце для працягу."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Твар распазнаны. Націсніце для працягу."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не ўдалося захаваць. Паўтарыце спробу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не ўдалося захаваць."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Скарыстайце не менш як 4 сімвалы"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Скарыстайце менш за 16 сімвалаў"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string> <string name="basic_status" msgid="2315371112182658176">"Адкрытая размова"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index a3397445745b..96646ada1ef2 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицето е удостоверено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потвърдено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Докоснете „Потвърждаване“ за завършване"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Отключено с лице."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Отключено с лице. Натиснете, за да продължите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицето бе разпознато. Натиснете, за да продължите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Лицето бе разпознато. Продължете чрез иконата за отключване."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не може да се запази. Опитайте отново."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не може да се запази."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Използвайте поне 4 знака"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Използвайте по-малко от 16 знака"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string> <string name="basic_status" msgid="2315371112182658176">"Отворен разговор"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 5209c18bc716..a53baad686a8 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ফেস যাচাই করা হয়েছে"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"কনফার্ম করা হয়েছে"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"সম্পূর্ণ করতে \'কনফার্ম করুন\' বোতামে ট্যাপ করুন"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ফেসের সাহায্যে আনলক করা হয়েছে।"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ফেসের সাহায্যে আনলক করা হয়েছে। চালিয়ে যেতে প্রেস করুন।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ফেস শনাক্ত করা হয়েছে। চালিয়ে যেতে প্রেস করুন।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ফেস শনাক্ত করা হয়েছে। চালিয়ে যেতে আনলক আইকন প্রেস করুন।"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"সেভ করা যাচ্ছে না। আবার চেষ্টা করুন।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"সেভ করা যাচ্ছে না।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"কমপক্ষে ৪টি অক্ষর ব্যবহার করুন"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"১৬টির চেয়ে কম অক্ষর ব্যবহার করুন"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string> <string name="basic_status" msgid="2315371112182658176">"খোলা কথোপকথন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index dfe025de4fdb..00790b38beca 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je provjereno"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi da završite"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Otključano je licem."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano licem. Pritisnite da nastavite."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice prepoznato. Pritisnite da nastavite."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Lice prepoznato. Pritisnite ikonu za otklj. da nastavite."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nije moguće sačuvati. Pokušajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nije moguće sačuvati."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite najmanje 4 znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string> <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a40073a1503b..74b557b624e5 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Cara autenticada"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirma per completar"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"S\'ha desbloquejat amb la cara."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S\'ha desbloquejat amb la cara. Prem per continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"S\'ha reconegut la cara. Prem per continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"S\'ha reconegut la cara. Prem la icona per continuar."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No es pot desar. Torna-ho a provar."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No es pot desar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilitza 4 caràcters com a mínim"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilitza menys de 16 caràcters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa oberta"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4271ccf26521..97bf39f19223 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Obličej byl ověřen"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrzeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ověření dokončíte klepnutím na Potvrdit"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Odemknuto obličejem."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odemknuto obličejem. Pokračujte stisknutím."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Obličej rozpoznán. Pokračujte stisknutím."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Obličej rozpoznán. Klepněte na ikonu odemknutí."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Uložení se nezdařilo. Zkuste to znovu."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Uložení se nezdařilo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Heslo musí mít alespoň 4 znaky"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Použijte méně než 16 znaků"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string> <string name="basic_status" msgid="2315371112182658176">"Otevřít konverzaci"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 2c3e041ee311..7388fbf6d8a8 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansigtet er godkendt"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekræftet"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tryk på Bekræft for at udføre"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Låst op ved hjælp af ansigt."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Låst op ved hjælp af ansigt. Tryk for at fortsætte."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansigt genkendt. Tryk for at fortsætte."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ansigt genkendt. Tryk på oplåsningsikonet for at fortsætte."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Der kan ikke gemmes. Prøv igen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Der kan ikke gemmes."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Angiv mindst 4 tegn"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Angiv højst 16 tegn"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string> <string name="basic_status" msgid="2315371112182658176">"Åben samtale"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index ee7d4e50b115..7608509fdbbd 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gesicht authentifiziert"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bestätigt"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Zum Abschließen auf \"Bestätigen\" tippen"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Gerät per Gesichtserkennung entsperrt."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Gerät mit dem Gesicht entsperrt. Tippe, um fortzufahren."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gesicht erkannt. Tippe, um fortzufahren."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Gesicht erkannt. Tippe zum Fortfahren auf das Symbol „Entsperren“."</string> @@ -467,7 +468,7 @@ <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Deine privaten Apps sind über <xliff:g id="VPN_APP">%1$s</xliff:g> mit dem Internet verbunden. Deine Netzwerkaktivitäten, einschließlich E-Mails und Browserdaten, sind für deinen VPN-Anbieter sichtbar."</string> <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string> <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN-Einstellungen öffnen"</string> - <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Gerätenutzungsdauer einsehen und verwalten."</string> + <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Bildschirmzeit einsehen und verwalten."</string> <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string> <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Durch TrustAgent entsperrt"</string> <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Speichern nicht möglich. Versuche es noch einmal."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Speichern nicht möglich."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gib mindestens vier Zeichen ein"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Maximal 16 Zeichen möglich"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string> <string name="basic_status" msgid="2315371112182658176">"Offene Unterhaltung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index da73c66924d0..e4187543b335 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Έγινε έλεγχος ταυτότητας προσώπου"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Επιβεβαιώθηκε"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Πατήστε Επιβεβαίωση για ολοκλήρωση"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Ξεκλειδώθηκε με αναγνώριση προσώπου."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ξεκλείδωμα με αναγνώριση προσώπου. Πατήστε για συνέχεια."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Το πρόσωπο αναγνωρίστηκε. Πατήστε για συνέχεια."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Το πρόσωπο αναγνωρ. Πατήστε το εικον. ξεκλειδ. για συνέχεια."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Δεν είναι δυνατή η αποθήκευση. Δοκιμάστε ξανά."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Δεν είναι δυνατή η αποθήκευση."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Χρησιμοποιήστε τουλάχιστον 4 χαρακτήρες"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Χρησιμοποιήστε λιγότερους από 16 χαρακτήρες"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string> <string name="basic_status" msgid="2315371112182658176">"Άνοιγμα συνομιλίας"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 2f8e3a4c5eb2..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Unlocked by face."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognised. Press the unlock icon to continue."</string> @@ -987,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index bcb1a99ae09a..d8971ba0d30d 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Unlocked by face."</string> + <string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"Unlocked by face"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognized. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognized. Press the unlock icon to continue."</string> @@ -1003,7 +1003,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least 4 characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 2f8e3a4c5eb2..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Unlocked by face."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognised. Press the unlock icon to continue."</string> @@ -987,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 2f8e3a4c5eb2..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Unlocked by face."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognised. Press the unlock icon to continue."</string> @@ -987,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index b6c3bc3559d6..368632b12250 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Unlocked by face."</string> + <string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"Unlocked by face"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognized. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognized. Press the unlock icon to continue."</string> @@ -1003,7 +1003,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least 4 characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index d43515f76f3f..06647b79ce78 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Se autenticó el rostro"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmado"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Presiona Confirmar para completar"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Se desbloqueó con rostro."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueo con rostro. Presiona para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rostro reconocido. Presiona para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Rostro reconocido. Presiona el desbloqueo para continuar."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Vuelve a intentarlo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa al menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string> <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 0acba5e4512f..c6b8d5081fae 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Cara autenticada"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirmar para completar la acción"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Desbloqueado con la cara."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado con la cara. Pulsa para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Cara reconocida. Pulsa para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Cara reconocida. Pulsa el icono de desbloquear para continuar."</string> @@ -988,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Sugerencias de dispositivos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Detén tu sesión compartida para transferir el contenido multimedia a otro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Detener"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Inténtalo de nuevo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa 4 caracteres como mínimo"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string> <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index a51a08570681..bb9198eeeaf6 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Nägu on autenditud"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Kinnitatud"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Lõpuleviimiseks puudutage nuppu Kinnita"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Avati näoga."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Avati näoga. Vajutage jätkamiseks."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Nägu tuvastati. Vajutage jätkamiseks."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Nägu tuvastati. Jätkamiseks vajutage avamise ikooni."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ei saa salvestada. Proovige uuesti."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ei saa salvestada."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Kasutage vähemalt 4 tähemärki"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kasutage vähem kui 16 tähemärki"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string> <string name="basic_status" msgid="2315371112182658176">"Avage vestlus"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index e02f3837374d..a24dfef0cf1b 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Autentifikatu da aurpegia"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Berretsita"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Amaitzeko, sakatu \"Berretsi\""</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Aurpegiaren bidez desblokeatu da."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Aurpegiaren bidez desblokeatu da. Sakatu aurrera egiteko."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ezagutu da aurpegia. Sakatu aurrera egiteko."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ezagutu da aurpegia. Aurrera egiteko, sakatu desblokeatzeko ikonoa."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ezin da gorde. Saiatu berriro."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ezin da gorde."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Erabili lau karaktere gutxienez"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Erabili 16 karaktere baino gutxiago"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string> <string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 5c9115e7d19c..1b029051fa00 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالتسنجی شد"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"قفل با چهره باز شد."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"قفلْ با چهره باز شد. برای ادامه، فشار دهید."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"چهره شناسایی شد. برای ادامه، فشار دهید."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"چهره شناسایی شد. برای ادامه، نماد قفلگشایی را فشار دهید."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ذخیره نشد. دوباره امتحان کنید."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ذخیره نشد."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"حداقل از ۴ نویسه استفاده کنید"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"از کمتر از ۱۶ نویسه استفاده کنید"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریدهدان کپی شد."</string> <string name="basic_status" msgid="2315371112182658176">"باز کردن مکالمه"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 7d4eb4e684db..91b074d1396f 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Kasvot tunnistettu"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Vahvistettu"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Valitse lopuksi Vahvista"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Avattu kasvojen avulla."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Avattu kasvojen avulla. Jatka painamalla."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Kasvot tunnistettu. Jatka painamalla."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Kasvot tunnistettu. Jatka lukituksen avauskuvakkeella."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tallennus ei onnistu. Yritä uudelleen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tallennus ei onnistu."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Käytä vähintään 4 merkkiä"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Käytä alle 16 merkkiä"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string> <string name="basic_status" msgid="2315371112182658176">"Avaa keskustelu"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 32b6a5909b13..7d2d92abf954 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Visage authentifié"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmé"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Touchez Confirmer pour terminer"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Déverrouillé avec le visage."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Déverr. par reconnaissance faciale. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Visage reconnu. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Visage reconnu. Appuyez sur Déverrouiller pour continuer."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilisez au moins 4 caractères"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilisez moins de 16 caractères"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string> <string name="basic_status" msgid="2315371112182658176">"Ouvrir la conversation"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 1b0b7c845032..496ae69342d5 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Visage authentifié"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmé"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Appuyez sur \"Confirmer\" pour terminer"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Déverrouillé par le visage."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Déverrouillé par visage. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Visage reconnu. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Visage reconnu. Appuyez sur l\'icône de déverrouillage pour continuer."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilisez au moins quatre caractères"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilisez moins de 16 caractères"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string> <string name="basic_status" msgid="2315371112182658176">"Conversation ouverte"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 8ab6596bb422..bca8d5173deb 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Autenticouse a cara"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirmar para completar o proceso"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Usouse o desbloqueo facial."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Usouse o desbloqueo facial. Preme para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Recoñeceuse a cara. Preme para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Recoñeceuse a cara. Preme a icona de desbloquear."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Non se puido gardar a información. Téntao de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Non se pode gardar a información."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utiliza como mínimo 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utiliza menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 32f3c6e9f183..8f86401574e0 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ચહેરાનું પ્રમાણીકરણ થયું"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"પુષ્ટિ કરી"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"પરીક્ષણ પૂર્ણ કરવા કન્ફર્મ કરોને ટૅપ કરો"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ચહેરા દ્વારા અનલૉક કર્યું."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ચહેરા દ્વારા અનલૉક કર્યું. આગળ વધવા માટે દબાવો."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ચહેરો ઓળખ્યો. આગળ વધવા માટે દબાવો."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ચહેરો ઓળખ્યો. આગળ વધવા \'અનલૉક કરો\' આઇકન દબાવો."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"સાચવી શકતા નથી. ફરી પ્રયાસ કરો."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"સાચવી શકતા નથી."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ઓછામાં ઓછા 4 અક્ષરનો ઉપયોગ કરો"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 કરતાં ઓછા અક્ષરનો ઉપયોગ કરો"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string> <string name="basic_status" msgid="2315371112182658176">"વાતચીત ખોલો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 885a77e50eec..b2d4b883f359 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"चेहरे की पुष्टि हो गई"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"पुष्टि हो गई"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"\'पुष्टि करें\' पर टैप करके पूरा करें"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"चेहरे से अनलॉक किया गया."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"चेहरे से अनलॉक किया गया. जारी रखने के लिए टैप करें."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"चेहरे की पहचान हो गई. जारी रखने के लिए टैप करें."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"चेहरे की पहचान हो गई. जारी रखने के लिए अनलॉक आइकॉन को टैप करें."</string> @@ -983,7 +984,7 @@ <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्टिंग करना रोकें"</string> + <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ऑडियो आउटपुट के लिए उपलब्ध डिवाइस."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"वॉल्यूम"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेव नहीं किया जा सका. फिर से कोशिश करें."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेव नहीं किया जा सका."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"कम से कम चार वर्ण इस्तेमाल करें"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 वर्ण से कम इस्तेमाल करें"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string> <string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 366785cf2e3b..54d87f282d11 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je autentificirano"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi za dovršetak"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Otključano licem."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano pomoću lica. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice je prepoznato. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Lice je prepoznato. Pritisnite ikonu otključavanja da biste nastavili."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Spremanje nije uspjelo. Pokušajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Spremanje nije uspjelo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Upotrijebite barem četiri znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Upotrijebite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string> <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 5e3f1ec3aa62..25b30a77e51e 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Arc hitelesítve"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Megerősítve"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Koppintson a Megerősítés lehetőségre"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Zárolás arccal feloldva."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Zárolás arccal feloldva. Koppintson a folytatáshoz."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Arc felismerve. Koppintson a folytatáshoz."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Arc felismerve. A folytatáshoz koppintson a Feloldásra."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"A mentés nem sikerült. Próbálja újra."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"A mentés nem sikerült."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Legalább négy karaktert használjon"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Legfeljebb 16 karaktert használhat"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string> <string name="basic_status" msgid="2315371112182658176">"Beszélgetés megnyitása"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 8dd835a7e4e0..14326af3f2e9 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Դեմքը ճանաչվեց"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Հաստատվեց"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ավարտելու համար հպեք «Հաստատել»"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Ապակողպվել է դեմքով։"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ապակողպվել է դեմքով։ Սեղմեք շարունակելու համար։"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Դեմքը ճանաչվեց։ Սեղմեք շարունակելու համար։"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Դեմքը ճանաչվեց։ Սեղմեք ապակողպման պատկերակը։"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Չհաջողվեց պահել։ Նորից փորձեք։"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Չհաջողվեց պահել։"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Օգտագործեք առնվազն 4 նիշ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Օգտագործեք ոչ ավել քան 16 նիշ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string> <string name="basic_status" msgid="2315371112182658176">"Բաց զրույց"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index cb48312403b4..f402df66558f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Wajah diautentikasi"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Dikonfirmasi"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ketuk Konfirmasi untuk menyelesaikan"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Kunci dibuka dengan wajah."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Kunci dibuka dengan wajah. Tekan untuk melanjutkan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Wajah dikenali. Tekan untuk melanjutkan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Wajah dikenali. Tekan ikon buka kunci untuk melanjutkan."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat menyimpan. Coba lagi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat menyimpan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gunakan minimal 4 karakter"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gunakan kurang dari 16 karakter"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string> <string name="basic_status" msgid="2315371112182658176">"Membuka percakapan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 3d17d8b1ec3c..5b755070ee57 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Andlit staðfest"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Staðfest"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ýttu á „Staðfesta“ til að ljúka"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Opnað með andliti."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Opnað með andliti. Ýttu til að halda áfram."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Andlitið var greint. Ýttu til að halda áfram."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Andlitið var greint. Ýttu á opnunartáknið til að halda áfr."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ekki hægt að vista. Reyndu aftur."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ekki hægt að vista."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Notaðu að minnsta kosti 4 stafi"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Notaðu færri en 16 stafi"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string> <string name="basic_status" msgid="2315371112182658176">"Opna samtal"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index a2345e58bd5f..8937c212e3ca 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Volto autenticato"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confermato"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tocca Conferma per completare"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Sbloccato con il volto."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Sbloccato con il volto. Premi per continuare."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Volto riconosciuto. Premi per continuare."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Volto riconosciuto. Premi l\'icona Sblocca e continua."</string> @@ -912,7 +913,7 @@ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Mostra altre app"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Riordina"</string> <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Aggiungi controlli"</string> - <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Torna alle modifiche"</string> + <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Torna alla modifica"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossibile caricare i controlli. Verifica nell\'app <xliff:g id="APP">%s</xliff:g> che le relative impostazioni non siano cambiate."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Controlli compatibili non disponibili"</string> <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altro"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossibile salvare. Riprova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossibile salvare."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa almeno 4 caratteri"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa meno di 16 caratteri"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string> <string name="basic_status" msgid="2315371112182658176">"Apri conversazione"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 45387583a4ae..271001dafd4b 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"זיהוי הפנים בוצע"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"יש אישור"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"יש להקיש על \'אישור\' לסיום התהליך"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"הנעילה בוטלה באמצעות זיהוי הפנים."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"הנעילה בוטלה באמצעות זיהוי הפנים. יש ללחוץ כדי להמשיך."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"הפנים זוהו. יש ללחוץ כדי להמשיך."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"הפנים זוהו. להמשך יש ללחוץ על סמל ביטול הנעילה."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"לא ניתן לשמור. כדאי לנסות שוב."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"לא ניתן לשמור."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"יש להזין 4 תווים לפחות"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"אפשר להזין עד 16 תווים"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"מספר Build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"מספר ה-Build הועתק ללוח."</string> <string name="basic_status" msgid="2315371112182658176">"פתיחת שיחה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index c97ed47d4754..88d0c600c0ce 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"顔を認証しました"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"確認しました"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"完了するには [確認] をタップしてください"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"顔でロック解除しました。"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"顔でロック解除しました。押して続行してください。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"顔を認識しました。押して続行してください。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"顔を認識しました。ロック解除アイコンを押して続行します。"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"保存できません。もう一度お試しください。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"保存できません。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"4 文字以上にしてください"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"使用できる文字数は 16 文字未満です"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string> <string name="basic_status" msgid="2315371112182658176">"空の会話"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index c40107edc4eb..e7fd9d3e00fa 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"სახის ამოცნობილია"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"დადასტურებული"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"დასასრულებლად შეეხეთ „დადასტურებას“"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"განიბლოკა სახით."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"განიბლოკა სახით. დააჭირეთ გასაგრძელებლად."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ამოცნობილია სახით. დააჭირეთ გასაგრძელებლად."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ამოცნობილია სახით. გასაგრძელებლად დააჭირეთ განბლოკვის ხატულას."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"შენახვა ვერ ხერხდება. ცადეთ ხელახლა."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"შენახვა ვერ ხერხდება."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"გამოიყენეთ მინიმუმ 4 სიმბოლო."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"გამოიყენეთ 16-ზე ნაკლები სიმბოლო"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string> <string name="basic_status" msgid="2315371112182658176">"მიმოწერის გახსნა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 9178d5facf84..9964f4de7f8b 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Бет танылды."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Расталды"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Аяқтау үшін \"Растау\" түймесін түртіңіз."</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Бетпен ашылды."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Бетпен ашылды. Жалғастыру үшін басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Бет танылды. Жалғастыру үшін басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Бет танылды. Жалғастыру үшін құлыпты ашу белгішесін басыңыз."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Сақталмайды. Қайталап көріңіз."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Сақталмайды."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Кемінде 4 таңба пайдаланыңыз."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Ең көбі 16 таңба пайдаланыңыз."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string> <string name="basic_status" msgid="2315371112182658176">"Ашық әңгіме"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index cbce5a063cd3..1165ba6e0b7d 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"បានផ្ទៀងផ្ទាត់មុខ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"បានបញ្ជាក់"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ចុច \"បញ្ជាក់\" ដើម្បីបញ្ចប់"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"បានដោះសោដោយប្រើមុខ។"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"បានដោះសោដោយប្រើមុខ។ សូមចុច ដើម្បីបន្ត។"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"បានស្គាល់មុខ។ សូមចុច ដើម្បីបន្ត។"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"បានស្គាល់មុខ។ សូមចុចរូបដោះសោ ដើម្បីបន្ត។"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"មិនអាចរក្សាទុកបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"មិនអាចរក្សាទុកបានទេ។"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ប្រើយ៉ាងហោចណាស់ 4 តួអក្សរ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ប្រើតិចជាង 16 តួអក្សរ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខកំណែបង្កើត"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខកំណែបង្កើតទៅឃ្លីបបត។"</string> <string name="basic_status" msgid="2315371112182658176">"បើកការសន្ទនា"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 400d2a79eb27..5d8e001e6153 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ಪೂರ್ಣಗೊಳಿಸಲು ದೃಢೀಕರಿಸಿ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದುವರಿಯಲು ಒತ್ತಿ."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ಮುಖ ಗುರುತಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಯಲು ಒತ್ತಿ."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ಮುಖ ಗುರುತಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಯಲು ಅನ್ಲಾಕ್ ಐಕಾನ್ ಅನ್ನು ಒತ್ತಿ."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ಕನಿಷ್ಠ 4 ಅಕ್ಷರಗಳನ್ನು ಬಳಸಿ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 ಕ್ಕಿಂತ ಕಡಿಮೆ ಅಕ್ಷರಗಳನ್ನು ಬಳಸಿ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string> <string name="basic_status" msgid="2315371112182658176">"ಸಂಭಾಷಣೆಯನ್ನು ತೆರೆಯಿರಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index f699dbfd8293..7329f89ca47e 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"얼굴이 인증되었습니다."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"확인함"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"완료하려면 확인을 탭하세요."</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"얼굴 인식으로 잠금 해제되었습니다."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"얼굴 인식으로 잠금 해제되었습니다. 계속하려면 누르세요."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"얼굴이 인식되었습니다. 계속하려면 누르세요."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"얼굴이 인식되었습니다. 계속하려면 아이콘을 누르세요."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"저장할 수 없습니다. 다시 시도해 주세요."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"저장할 수 없습니다."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"4자 이상 입력하세요."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16자 미만이어야 합니다."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string> <string name="basic_status" msgid="2315371112182658176">"대화 열기"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 402f25d765e7..751c98c9b3fa 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Жүздүн аныктыгы текшерилди"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Ырасталды"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Бүтүрүү үчүн \"Ырастоо\" баскычын басыңыз"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Түзмөгүңүздү жүзүңүз менен ачтыңыз."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Кулпуну жүзүңүз менен ачтыңыз. Улантуу үчүн басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Жүз таанылды. Улантуу үчүн басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Жүз таанылды. Улантуу үчүн кулпусун ачуу сүрөтчөсүн басыңыз."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Сакталган жок. Кайталап көрүңүз."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Сакталган жок."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Кеминде 4 символдон турушу керек"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 символдон ашпашы керек"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string> <string name="basic_status" msgid="2315371112182658176">"Ачык сүйлөшүү"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 62e649d149b1..e075dba9c37a 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ຢືນຢັນແລ້ວ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ແຕະຢືນຢັນເພື່ອສຳເລັດ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ປົດລັອກດ້ວຍໃບໜ້າແລ້ວ."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດເພື່ອສືບຕໍ່."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ຈຳແນກໜ້າໄດ້ແລ້ວ. ກົດເພື່ອສືບຕໍ່."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ຈຳແນກໜ້າໄດ້ແລ້ວ. ກົດໄອຄອນປົດລັອກເພື່ອສືບຕໍ່."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ບໍ່ສາມາດບັນທຶກໄດ້. ກະລຸນາລອງໃໝ່."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ບໍ່ສາມາດບັນທຶກໄດ້."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ໃຊ້ຢ່າງໜ້ອຍ 4 ຕົວອັກສອນ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ໃຊ້ໜ້ອຍກວ່າ 16 ຕົວອັກສອນ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string> <string name="basic_status" msgid="2315371112182658176">"ເປີດການສົນທະນາ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index aa7902275d50..57bb64e60073 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Veidas autentifikuotas"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Patvirtinta"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Paliesk. „Patvirtinti“, kad užbaigtumėte"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Atrakinta pagal veidą."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Atrakinta pagal veidą. Paspauskite, jei norite tęsti."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Veidas atpažintas. Paspauskite, jei norite tęsti."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Veidas atpažintas. Tęskite paspaudę atrakinimo piktogramą."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nepavyko išsaugoti. Bandykite dar kartą."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nepavyko išsaugoti."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Naudokite bent 4 simbolius"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Naudokite daugiausia 16 simbolių"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijos numeris"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas į iškarpinę."</string> <string name="basic_status" msgid="2315371112182658176">"Atidaryti pokalbį"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index cb5a0e6093a0..78fc7b3761b6 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Seja autentificēta"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Apstiprināts"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Lai pabeigtu, pieskarieties Apstiprināt"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Ierīce atbloķēta pēc sejas."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ierīce atbloķēta ar seju. Nospiediet, lai turpinātu."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Seja atpazīta. Nospiediet, lai turpinātu."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Seja atpazīta. Lai turpinātu, nospiediet atbloķēšanas ikonu."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nevar saglabāt. Mēģiniet vēlreiz."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nevar saglabāt."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Izmantojiet vismaz 4 rakstzīmes"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Izmantojiet mazāk nekā 16 rakstzīmes"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string> <string name="basic_status" msgid="2315371112182658176">"Atvērt sarunu"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index ec4a6c42f18d..6ddd15c3b9f1 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицето е проверено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потврдено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Допрете „Потврди“ за да се заврши"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Отклучено со лице."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Отклучено со лик. Притиснете за да продолжите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицето е препознаено. Притиснете за да продолжите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Лицето е препознаено. Притиснете ја иконата за отклучување за да продолжите."</string> @@ -173,12 +174,12 @@ <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Поставување „Отклучување со отпечаток“"</string> <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"За да поставите „Отклучување со отпечаток“ повторно, вашите сегашни слики и модели на отпечаток ќе се избришат.\n\nОткако ќе се избришат, ќе треба повторно да поставите „Отклучување со отпечаток“ за да го користите отпечатокот за отклучување на телефонот или потврда дека сте вие."</string> <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"За да поставите „Отклучување со отпечаток“ повторно, вашите сегашните слики и модели на отпечаток ќе бидат избришат.\n\nОткако ќе се избришат, ќе треба повторно да поставите „Отклучување со отпечаток“ за да го користите отпечатокот за да го отклучите телефонот или да потврдите дека сте вие."</string> - <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Не можеше да се постави „Отклучување со отпечаток“. Одете во „Поставки“ за да се обидете повторно."</string> + <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Не можеше да се постави „Отклучување со отпечаток“. Отворете „Поставки“ за да се обидете повторно."</string> <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Поставете „Отклучување со лик“ повторно"</string> <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Отклучување со лик"</string> <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Поставување „Отклучување со лик“"</string> <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"За да може одново да поставите „Отклучување со лик“, вашиот сегашен модел на лик ќе се избрише.\n\nЗа да го користите ликот за отклучување на телефонот, ќе треба повторно да ја поставите функцијава."</string> - <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Не можеше да се постави „Отклучување со лик“. Одете во „Поставки“ за да се обидете повторно."</string> + <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Не можеше да се постави „Отклучување со лик“. Отворете „Поставки“ за да се обидете повторно."</string> <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Допрете го сензорот за отпечатоци"</string> <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Не се препознава ликот. Користете отпечаток."</string> <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) --> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не може да се зачува. Обидете се повторно."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не може да се зачува."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Употребете најмалку 4 знаци"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Употребете помалку од 16 знаци"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string> <string name="basic_status" msgid="2315371112182658176">"Започни разговор"</string> @@ -1144,7 +1146,7 @@ <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"Не може да повикувате од лична апликација"</string> - <string name="call_from_work_profile_text" msgid="2856337395968118274">"Вашата организацијата ви дозволува да упатувате повици само од работни апликации"</string> + <string name="call_from_work_profile_text" msgid="2856337395968118274">"Вашата организација ви дозволува да упатувате повици само од работни апликации"</string> <string name="call_from_work_profile_action" msgid="2937701298133010724">"Префрли се на работен профил"</string> <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Инсталирајте работна апликација на телефон"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"Откажи"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 9bc496ce7987..467720ae18be 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"സ്ഥിരീകരിച്ചു"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"പൂർത്തിയാക്കാൻ സ്ഥിരീകരിക്കുക ടാപ്പ് ചെയ്യൂ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തിരിക്കുന്നു."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തു. തുടരാൻ അമർത്തുക."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"മുഖം തിരിച്ചറിഞ്ഞു. തുടരാൻ അമർത്തുക."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"മുഖം തിരിച്ചറിഞ്ഞു. തുടരാൻ അൺലോക്ക് ഐക്കൺ അമർത്തുക."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"സംരക്ഷിക്കാൻ കഴിയില്ല. വീണ്ടും ശ്രമിക്കുക."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"സംരക്ഷിക്കാൻ കഴിയില്ല."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"കുറഞ്ഞത് 4 പ്രതീകങ്ങളെങ്കിലും ഉപയോഗിക്കുക"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16-ൽ കുറവ് പ്രതീകങ്ങൾ ഉപയോഗിക്കുക"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string> <string name="basic_status" msgid="2315371112182658176">"സംഭാഷണം തുറക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 620883362746..d17933aff733 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Царайг баталгаажууллаа"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Баталгаажсан"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Дуусгахын тулд баталгаажуулахыг товших"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Царайгаар түгжээг тайлсан."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Царайгаар түгжээг тайлсан. Үргэлжлүүлэхийн тулд дарна уу."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Царайг таньсан. Үргэлжлүүлэхийн тулд дарна уу."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Царайг таньсан. Үргэлжлүүлэх бол түгжээг тайлах дүрсийг дар."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Хадгалах боломжгүй. Дахин оролдоно уу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Хадгалах боломжгүй."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Дор хаяж 4 тэмдэгт ашиглана уу."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16-аас цөөн тэмдэгт ашиглана уу"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийцийн дугаар"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Хийцийн дугаарыг түр санах ойд хуулсан."</string> <string name="basic_status" msgid="2315371112182658176">"Харилцан яриаг нээх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index eee6bc3290da..b1825e9e6096 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"चेहरा ऑथेंटिकेशन केलेला आहे"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"निश्चित केले"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"पूर्ण करण्यासाठी खात्री करा वर टॅप करा"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"चेहऱ्याने अनलॉक केले आहे."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"चेहऱ्याने अनलॉक केले आहे. पुढे सुरू ठेवण्यासाठी प्रेस करा."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"चेहरा ओळखला आहे. पुढे सुरू ठेवण्यासाठी प्रेस करा."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"चेहरा ओळखला आहे. पुढे सुरू ठेवण्यासाठी अनलॉक करा आयकन प्रेस करा."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेव्ह करू शकत नाही. पुन्हा प्रयत्न करा."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेव्ह करू शकत नाही."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"किमान चार वर्ण वापरा"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"१६ पेक्षा कमी वर्ण वापरा"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string> <string name="basic_status" msgid="2315371112182658176">"संभाषण उघडा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index ab68d0aa9579..f12b476a4cab 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Wajah disahkan"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Disahkan"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ketik Sahkan untuk menyelesaikan"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Dibuka kunci dengan wajah."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Dibuka kunci dengan wajah. Tekan untuk meneruskan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Wajah dicam. Tekan untuk meneruskan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Wajah dicam. Tekan ikon buka kunci untuk meneruskan."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat disimpan. Cuba lagi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat disimpan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gunakan sekurang-kurangnya 4 aksara"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gunakan kurang daripada 16 aksara"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string> <string name="basic_status" msgid="2315371112182658176">"Buka perbualan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index c137069e6b7a..1d85be1fca86 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"အတည်ပြုပြီးပြီ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"အပြီးသတ်ရန်အတွက် \'အတည်ပြုရန်\' ကို တို့ပါ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"မျက်နှာဖြင့် ဖွင့်ထားသည်။"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"မျက်နှာဖြင့် ဖွင့်ထားသည်။ ရှေ့ဆက်ရန် နှိပ်ပါ။"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"မျက်နှာ မှတ်မိသည်။ ရှေ့ဆက်ရန် နှိပ်ပါ။"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"မျက်နှာ မှတ်မိသည်။ ရှေ့ဆက်ရန် လော့ခ်ဖွင့်သင်္ကေတကို နှိပ်ပါ။"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"သိမ်း၍မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"သိမ်း၍မရပါ။"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"အနည်းဆုံး အက္ခရာ ၄ လုံး သုံးရန်"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"အက္ခရာ ၁၆ လုံးအောက် သုံးရန်"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string> <string name="basic_status" msgid="2315371112182658176">"စကားဝိုင်းကို ဖွင့်ရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index de150fa972fb..2363a689777b 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansiktet er autentisert"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekreftet"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Trykk på Bekreft for å fullføre"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Låst opp med ansiktet."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Låst opp med ansiktet. Trykk for å fortsette."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansiktet er gjenkjent. Trykk for å fortsette."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ansiktet er gjenkjent. Trykk på lås opp-ikon for å fortsette"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan ikke lagre. Prøv på nytt."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan ikke lagre."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Bruk minst 4 tegn"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Bruk færre enn 16 tegn"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string> <string name="basic_status" msgid="2315371112182658176">"Åpen samtale"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 9e519b34dfef..2f99cc73979f 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"अनुहार प्रमाणीकरण गरियो"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"पुष्टि भयो"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"पूरा गर्नका लागि पुष्टि गर्नुहोस् नामक विकल्पमा ट्याप गर्नुहोस्"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"अनुहार प्रयोग गरी अनलक गरियो।"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"अनुहार प्रयोग गरी अनलक गरियो। जारी राख्न थिच्नुहोस्।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"अनुहार पहिचान गरियो। जारी राख्न थिच्नुहोस्।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"अनुहार पहिचान गरियो। जारी राख्न अनलक आइकनमा थिच्नुहोस्।"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेभ गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेभ गर्न सकिएन।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"कम्तीमा ४ वटा वर्ण प्रयोग गर्नुहोस्"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"१६ वटाभन्दा कम वर्ण प्रयोग गर्नुहोस्"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string> <string name="basic_status" msgid="2315371112182658176">"वार्तालाप खोल्नुहोस्"</string> @@ -1144,7 +1146,7 @@ <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string> <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"व्यक्तिगत एपमार्फत कल गर्न मिल्दैन"</string> - <string name="call_from_work_profile_text" msgid="2856337395968118274">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिन्छ"</string> + <string name="call_from_work_profile_text" msgid="2856337395968118274">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिएको छ"</string> <string name="call_from_work_profile_action" msgid="2937701298133010724">"कार्य प्रोफाइल प्रयोग गर्नुहोस्"</string> <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"कामसम्बन्धी फोन एप इन्स्टल गर्नुहोस्"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"रद्द गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 4ca6c01e45fa..05f7922c6d1c 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gezicht geverifieerd"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bevestigd"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tik op Bevestigen om te voltooien"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Ontgrendeld via gezicht."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ontgrendeld via gezicht. Druk om door te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gezicht herkend. Druk om door te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Gezicht herkend. Druk op het ontgrendelicoon."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan niet opslaan. Probeer het opnieuw."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan niet opslaan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 tekens"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder dan 16 tekens"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummer naar klembord gekopieerd."</string> <string name="basic_status" msgid="2315371112182658176">"Gesprek openen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 82814819d670..7f443de1898f 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ମୁହଁ ପ୍ରାମାଣିକତା ହୋଇଛି"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ସୁନିଶ୍ଚିତ କରାଯାଇଛି"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ସୁନିଶ୍ଚିତ କରନ୍ତୁରେ ଟାପ୍ କରନ୍ତୁ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି।"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ ଦବାନ୍ତୁ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ଫେସ ଚିହ୍ନଟ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ ଦବାନ୍ତୁ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ଫେସ ଚିହ୍ନଟ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ।"</string> @@ -296,7 +297,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g>ରେ ଅନ୍ ହେବ"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ଗାଢ଼ା ଥିମ"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ବ୍ୟାଟେରୀ ସେଭର୍"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ବେଟେରୀ ସେଭର"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ସନ୍ଧ୍ୟାରେ ଚାଲୁ ହେବ"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string> @@ -414,7 +415,7 @@ <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ଦ୍ୱାରା ବ୍ଲକ କରାଯାଇଛି"</string> <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"ଡିଭାଇସ ନୀତି ଦ୍ୱାରା ସ୍କ୍ରିନ କେପଚରିଂକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string> - <string name="clear_all_notifications_text" msgid="348312370303046130">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string> + <string name="clear_all_notifications_text" msgid="348312370303046130">"ସବୁ ଖାଲି କରନ୍ତୁ"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"ନୂଆ"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ସେଭ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ସେଭ କରାଯାଇପାରିଲା ନାହିଁ।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ଅତିକମରେ 4ଟି କେରେକ୍ଟର ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16ଟିରୁ କମ କେରେକ୍ଟର ବ୍ୟବହାର କରନ୍ତୁ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string> <string name="basic_status" msgid="2315371112182658176">"ବାର୍ତ୍ତାଳାପ ଖୋଲନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index b3d2b2f2d34c..f7bb0c916458 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਿਰਤ ਹੋਇਆ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ਪੂਰਾ ਕਰਨ ਲਈ ਪੁਸ਼ਟੀ ਕਰੋ \'ਤੇ ਟੈਪ ਕਰੋ"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ।"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਜਾਰੀ ਰੱਖਣ ਲਈ ਦਬਾਓ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਹੋਈ। ਜਾਰੀ ਰੱਖਣ ਲਈ ਦਬਾਓ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਹੋਈ। ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਅਣਲਾਕ ਕਰੋ\' ਪ੍ਰਤੀਕ ਨੂੰ ਦਬਾਓ।"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ਘੱਟੋ-ਘੱਟ 4 ਅੱਖਰ-ਚਿੰਨ੍ਹ ਵਰਤੋ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 ਤੋਂ ਘੱਟ ਅੱਖਰ-ਚਿੰਨ੍ਹ ਵਰਤੋ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string> <string name="basic_status" msgid="2315371112182658176">"ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 5830ee987172..e6af3fcf5f90 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Twarz rozpoznana"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potwierdzono"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Aby zakończyć, kliknij Potwierdź"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Odblokowano skanem twarzy."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odblokowano rozpoznawaniem twarzy. Kliknij, aby kontynuować."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Twarz rozpoznana. Kliknij, aby kontynuować."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Twarz rozpoznana. Aby kontynuować, kliknij ikonę odblokowywania."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nie można zapisać. Spróbuj ponownie."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nie można zapisać."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Wpisz co najmniej 4 znaki"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Wpisz mniej niż 16 znaków"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string> <string name="basic_status" msgid="2315371112182658176">"Otwarta rozmowa"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 16a3d052a4bd..b9df66103f3f 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em \"Confirmar\" para concluir"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Desbloqueado pelo rosto."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado pelo rosto. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Rosto reconhecido. Pressione o ícone para continuar."</string> @@ -988,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use pelo menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index dee32a5836a4..9c11d6150cd8 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmado"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em Confirmar para concluir."</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Desbloqueado com o rosto."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado com o rosto. Prima para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Prima para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Rosto reconhecido. Prima ícone de desbloqueio para continuar"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Não é possível guardar. Tente novamente."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Não é possível guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use, pelo menos, 4 carateres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 carateres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Abrir conversa"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 16a3d052a4bd..b9df66103f3f 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em \"Confirmar\" para concluir"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Desbloqueado pelo rosto."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado pelo rosto. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Rosto reconhecido. Pressione o ícone para continuar."</string> @@ -988,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use pelo menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index a388637029cb..9667f43a58c7 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Chip autentificat"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atinge Confirm pentru a finaliza"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"S-a deblocat folosind fața."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S-a deblocat cu ajutorul feței. Apasă pentru a continua."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Chipul a fost recunoscut. Apasă pentru a continua."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Chip recunoscut. Apasă pictograma Deblocare ca să continui."</string> @@ -987,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispozitive disponibile pentru ieșire audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și afișaje"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și ecrane"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Oprește sesiunea comună ca să muți elementul media pe alt dispozitiv"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Oprește"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nu se poate salva. Încearcă din nou."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nu se poate salva."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Folosește minimum 4 caractere."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Folosește maximum 16 caractere"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Deschide conversația"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 29ab7058d33d..8945e34232b1 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицо распознано"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Подтверждено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Нажмите \"Подтвердить\""</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Разблокировано сканированием лица."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Разблокировано сканированием лица. Нажмите, чтобы продолжить."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицо распознано. Нажмите, чтобы продолжить."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Лицо распознано. Нажмите на значок разблокировки."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не удалось сохранить. Повторите попытку."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не удалось сохранить."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Минимальное количество символов – 4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Максимальное количество символов – 16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string> <string name="basic_status" msgid="2315371112182658176">"Открытый чат"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index fd63ffc610e0..ef291cba38e8 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"මුහුණ සත්යාපන කළා"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"තහවුරු කළා"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"සම්පූර්ණ කිරීමට තහවුරු කරන්න තට්ටු කර."</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"මුහුණ මගින් අගුළු හරින ලදි."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"මුහුණ මගින් අගුලු හරින ලදි. ඉදිරියට යාමට ඔබන්න."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"මුහුණ හඳුනා ගන්නා ලදි. ඉදිරියට යාමට ඔබන්න."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"මුහුණ හඳුනා ගන්නා ලදි. ඉදිරියට යාමට අගුලු හැරීමේ නිරූපකය ඔබන්න."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"සුරැකිය නොහැකිය. නැවත උත්සාහ කරන්න."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"සුරැකිය නොහැකිය."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"අවම වශයෙන් අනුලකුණු 4ක් භාවිතා කරන්න"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"අනුලකුණු 16කට වඩා අඩුවෙන් භාවිතා කරන්න"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string> <string name="basic_status" msgid="2315371112182658176">"සංවාදය විවෘත කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 1cc3789d6020..2544505e4d4b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Tvár bola overená"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrdené"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Overenie dokončíte klepnutím na Potvrdiť"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Odomknuté tvárou."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odomknuté tvárou. Pokračujte stlačením."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Tvár bola rozpoznaná. Pokračujte stlačením."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Tvár bola rozpoznaná. Pokračujte stlačením ikony odomknutia"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nedá sa uložiť. Skúste to znova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nedá sa uložiť."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Použite aspoň štyri znaky"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Použite menej než 16 znakov"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string> <string name="basic_status" msgid="2315371112182658176">"Otvorená konverzácia"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 62769ff4db2a..9a9676fec3e3 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Pristnost obraza je potrjena"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potrjeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Za dokončanje se dotaknite »Potrdite«"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Odklenjeno z obrazom."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odklenjeno z obrazom. Pritisnite za nadaljevanje."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Obraz je prepoznan. Pritisnite za nadaljevanje."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Obraz je prepoznan. Za nadaljevanje pritisnite ikono za odklepanje."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ni mogoče shraniti. Poskusite znova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ni mogoče shraniti."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Uporabite vsaj 4 znake."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Uporabite manj kot 16 znakov."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string> <string name="basic_status" msgid="2315371112182658176">"Odprt pogovor"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index a721f6e10f04..d477aa415067 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Fytyra u vërtetua"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Konfirmuar"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Trokit \"Konfirmo\" për ta përfunduar"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"U shkyç me fytyrë."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"U shkyç me fytyrë. Shtyp për të vazhduar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Fytyra u njoh. Shtyp për të vazhduar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Fytyra u njoh. Shtyp ikonën e shkyçjes për të vazhduar."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nuk mund të ruhet. Provo përsëri."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nuk mund të ruhet."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Përdor të paktën 4 karaktere"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Përdor më pak se 16 karaktere"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string> <string name="basic_status" msgid="2315371112182658176">"Hap bisedën"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 9985ef052bfa..b293e6aaae02 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лице је потврђено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потврђено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Додирните Потврди да бисте завршили"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Откључано је лицем."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Откључано је лицем. Притисните да бисте наставили."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лице је препознато. Притисните да бисте наставили."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Лице препознато. Притисните икону откључавања за наставак."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Чување није успело. Пробајте поново."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Чување није успело."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Користите бар 4 знака"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Користите мање од 16 знакова"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string> <string name="basic_status" msgid="2315371112182658176">"Отворите конверзацију"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 9a087501e198..00a2e3c158bb 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansiktet har autentiserats"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekräftat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Slutför genom att trycka på Bekräfta"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Upplåst med ansiktslås."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Upplåst med ansiktslås. Tryck för att fortsätta."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansiktet har identifierats. Tryck för att fortsätta."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ansiktet har identifierats. Tryck på ikonen lås upp."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Det gick inte att spara. Försök igen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Det gick inte att spara."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Använd minst 4 tecken"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Använd färre än 16 tecken"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string> <string name="basic_status" msgid="2315371112182658176">"Öppen konversation"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index ad4125eff03d..af90c67aefb9 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Uso umethibitishwa"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Imethibitishwa"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Gusa Thibitisha ili ukamilishe"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Imefunguka kwa kutumia uso wako."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Imefunguliwa kwa kutumia uso wako. Bonyeza ili uendelee."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Uso umetambuliwa. Bonyeza ili uendelee."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Uso umetambuliwa. Bonyeza aikoni ya kufungua ili uendelee."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Imeshindwa kuhifadhi. Jaribu tena."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Imeshindwa kuhifadhi."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Tumia angalau herufi 4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Tumia herufi chini ya 16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string> <string name="basic_status" msgid="2315371112182658176">"Fungua mazungumzo"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 066c8d4c9e9d..fd33af97be2c 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"முகம் அங்கீகரிக்கப்பட்டது"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"உறுதிப்படுத்தப்பட்டது"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"முடிக்க \'உறுதிப்படுத்துக\' என்பதை தட்டவும்"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"முகம் மூலம் அன்லாக் செய்யப்பட்டது."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. தொடர அழுத்தவும்."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"முகம் அங்கீகரிக்கப்பட்டது. தொடர அழுத்தவும்."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"முகம் அங்கீகரிக்கப்பட்டது. தொடர அன்லாக் ஐகானை அழுத்தவும்."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"சேமிக்க முடியவில்லை. மீண்டும் முயலவும்."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"சேமிக்க முடியவில்லை."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"குறைந்தது 4 எழுத்துகளைப் பயன்படுத்துங்கள்"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 எழுத்துகளுக்குக் குறைவாகப் பயன்படுத்துங்கள்"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string> <string name="basic_status" msgid="2315371112182658176">"திறந்தநிலை உரையாடல்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index dc8c76366dd3..63fc9e03bfce 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ముఖం ప్రామాణీకరించబడింది"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"నిర్ధారించబడింది"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"పూర్తి చేయడానికి \"నిర్ధారించు\" నొక్కండి"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ముఖం ద్వారా అన్లాక్ చేయబడింది."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ముఖం ద్వారా అన్లాక్ చేయబడింది. కొనసాగించడానికి నొక్కండి."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ముఖం గుర్తించబడింది. కొనసాగించడానికి నొక్కండి."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"ముఖం గుర్తించబడింది. కొనసాగడానికి అన్లాక్ చిహ్నం నొక్కండి."</string> @@ -389,7 +390,7 @@ <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్లో ఉన్నారు"</string> <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"కొత్త యూజర్ను జోడించడం వలన గెస్ట్ మోడ్ నుండి వైదొలుగుతుంది. అలాగే ప్రస్తుత గెస్ట్ సెషన్ నుండి అన్ని యాప్లతో పాటు మొత్తం డేటా తొలగించబడుతుంది."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string> - <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ఒక యూజర్ను మాత్రమే క్రియేట్ చేయవచ్చు.}other{మీరు గరిష్టంగా # మంది యూజర్లను జోడించవచ్చు.}}"</string> + <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ఒక యూజర్ను మాత్రమే క్రియేట్ చేయవచ్చు.}other{మీరు గరిష్ఠంగా # మంది యూజర్లను జోడించవచ్చు.}}"</string> <string name="user_remove_user_title" msgid="9124124694835811874">"యూజర్ను తీసివేయాలా?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"ఈ వినియోగదారుకు సంబంధించిన అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"తీసివేయండి"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"సేవ్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"సేవ్ చేయడం సాధ్యపడదు."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"కనీసం 4 అక్షరాలను ఉపయోగించండి"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 కంటే తక్కువ అక్షరాలను ఉపయోగించండి"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్బోర్డ్కు కాపీ చేయబడింది."</string> <string name="basic_status" msgid="2315371112182658176">"సంభాషణను తెరవండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 793a1f7bdf85..49f0f2a85802 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ยืนยันแล้ว"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"แตะยืนยันเพื่อดำเนินการให้เสร็จสมบูรณ์"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"ปลดล็อกด้วยใบหน้าแล้ว"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ปลดล็อกด้วยใบหน้าแล้ว กดเพื่อดำเนินการต่อ"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"จดจำใบหน้าได้ กดเพื่อดำเนินการต่อ"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"จดจำใบหน้าได้ กดไอคอนปลดล็อกเพื่อดำเนินการต่อ"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"บันทึกไม่ได้ โปรดลองอีกครั้ง"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"บันทึกไม่ได้"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ใช้อักขระอย่างน้อย 4 ตัว"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ใช้อักขระไม่เกิน 16 ตัว"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิลด์"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิลด์ไปยังคลิปบอร์ดแล้ว"</string> <string name="basic_status" msgid="2315371112182658176">"เปิดการสนทนา"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 4dd75fe58a73..755c87207b9a 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Na-authenticate ang mukha"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Nakumpirma"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"I-tap ang Kumpirmahin para kumpletuhin"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Na-unlock gamit ang mukha."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Na-unlock gamit ang mukha. Pindutin para magpatuloy."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Nakilala ang mukha. Pindutin para magpatuloy."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Nakilala ang mukha. Pindutin ang unlock para magpatuloy."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Hindi ma-save. Subukan ulit."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Hindi ma-save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gumamit ng hindi bababa sa 4 na character"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gumamit ng mas kaunti sa 16 na character"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string> <string name="basic_status" msgid="2315371112182658176">"Buksan ang pag-uusap"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 6cec2a67bb3e..72c2d436d9b8 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Yüz kimliği doğrulandı"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Onaylandı"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tamamlamak için Onayla\'ya dokunun"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Cihazın kilidini yüzünüzle açtınız."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Cihazın kilidini yüzünüzle açtınız. Devam etmek için basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Yüzünüz tanındı. Devam etmek için basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Yüzünüz tanındı. Kilit açma simgesine basın."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kaydedilemiyor. Tekrar deneyin."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kaydedilemiyor."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"En az 4 karakter kullanın."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"En fazla 16 karakter kullanın"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string> <string name="basic_status" msgid="2315371112182658176">"Görüşmeyi aç"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index eb791ae0925a..c1e291d3fce7 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Обличчя автентифіковано"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Підтверджено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Щоб завершити, натисніть \"Підтвердити\""</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Розблоковано (фейс-контроль)."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Розблоковано (фейсконтроль). Натисніть, щоб продовжити."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Обличчя розпізнано. Натисніть, щоб продовжити."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Обличчя розпізнано. Натисніть значок розблокування."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не вдалося зберегти. Повторіть спробу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не вдалося зберегти."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Введіть принаймні 4 символи"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Введіть менше ніж 16 символів"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string> <string name="basic_status" msgid="2315371112182658176">"Відкрита розмова"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 8b03b84b266b..46596c34a250 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چہرے کی تصدیق ہو گئی"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تصدیق شدہ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"مکمل کرنے کیلئے \'تصدیق کریں\' تھپتھپائیں"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"چہرے سے غیر مقفل کیا گیا۔"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"چہرے سے انلاک کیا گیا۔ جاری رکھنے کے لیے دبائیں۔"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"چہرے کی شناخت ہو گئی۔ جاری رکھنے کے لیے دبائیں۔"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"چہرے کی شناخت ہو گئی۔ جاری رکھنے کیلئے انلاک آئیکن دبائیں۔"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"محفوظ نہیں کیا جا سکا۔ پھر کوشش کریں۔"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"محفوظ نہیں کیا جا سکا۔"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"کم از کم 4 حروف استعمال کریں"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 حروف سے کم استعمال کریں"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string> <string name="basic_status" msgid="2315371112182658176">"گفتگو کھولیں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index f703ed5dd6a5..ab1fe063a797 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Yuzingiz aniqlandi"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Tasdiqlangan"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tasdiqlash uchun tegining"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Yuz bilan ochildi."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Yuz orqali ochildi. Davom etish uchun bosing."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Yuz aniqlandi. Davom etish uchun bosing."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Yuz aniqlandi. Davom etish uchun ochish belgisini bosing."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Saqlanmadi. Qayta urining."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Saqlanmadi."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Parolga kamida 4 ta belgi kiriting."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kiritiladigan belgilar 16 tadan oshmasin"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string> <string name="basic_status" msgid="2315371112182658176">"Suhbatni ochish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index cc7aa7f08837..e681b1a724a0 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Đã xác thực khuôn mặt"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Ðã xác nhận"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Nhấn vào Xác nhận để hoàn tất"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Đã mở khoá bằng khuôn mặt."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Đã mở khoá bằng khuôn mặt. Hãy nhấn để tiếp tục."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Đã nhận diện khuôn mặt. Hãy nhấn để tiếp tục."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Đã nhận diện khuôn mặt. Nhấn biểu tượng mở khoá để tiếp tục."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Không lưu được. Hãy thử lại."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Không lưu được."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Sử dụng ít nhất 4 ký tự"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Sử dụng ít hơn 16 ký tự"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào bảng nhớ tạm."</string> <string name="basic_status" msgid="2315371112182658176">"Mở cuộc trò chuyện"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 40b069412771..5df113b5f5e7 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"面孔身份验证成功"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"已确认"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"点按“确认”即可完成"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"已用面孔解锁"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"已通过面孔识别解锁。点按即可继续。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"识别出面孔。点按即可继续。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"识别出面孔。按下解锁图标即可继续。"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"无法保存,请重试。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"无法保存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"必须至少 4 个字符"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"必须少于 16 个字符"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build 号"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"已将 Build 号复制到剪贴板。"</string> <string name="basic_status" msgid="2315371112182658176">"开放式对话"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 23c520a20ce3..f911d5c2c559 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"面孔已經驗證"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"已確認"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"輕按 [確定] 以完成"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"已使用面孔解鎖。"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"已使用面孔解鎖。按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"已識別面孔。按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"已識別面孔。按解鎖圖示即可繼續。"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"無法儲存,請再試一次。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"無法儲存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"請至少使用 4 個字元"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"請使用少於 16 個字元"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string> <string name="basic_status" msgid="2315371112182658176">"開啟對話"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index ec65f9a9b062..01cd254a4198 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"臉孔驗證成功"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"確認完畢"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"輕觸 [確認] 完成驗證設定"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"裝置已由人臉解鎖。"</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"裝置已透過你的臉解鎖,按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"臉孔辨識完成,按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"臉孔辨識完成,按下「解鎖」圖示即可繼續操作。"</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"無法儲存,請再試一次。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"無法儲存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"至少要有 4 個半形字元"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"不得超過 16 個半形字元"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string> <string name="basic_status" msgid="2315371112182658176">"開放式對話"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 1ca5c0504cb8..3f89ba951753 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -142,7 +142,8 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ubuso bufakazelwe ubuqiniso"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Kuqinisekisiwe"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Thepha okuthi Qinisekisa ukuze uqedele"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="3783056044917913453">"Kuvulwe ngobuso."</string> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> + <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Vula ngobuso. Cindezela ukuze uqhubeke."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ubuso buyaziwa. Cindezela ukuze uqhubeke."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ubuso buyaziwa. Cindezela isithonjana sokuvula ukuze uqhubeke."</string> @@ -1003,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ayikwazi ukulondoloza. Zama futhi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ayikwazi ukulondoloza."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Sebenzisa okungenani izinhlamvu ezi-4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Sebenzisa isinhlamvu ezimbalwa kuneziyi-16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string> <string name="basic_status" msgid="2315371112182658176">"Vula ingxoxo"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c3651cfa36e7..166bd2ac4439 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -902,4 +902,16 @@ <!-- Time (in ms) to delay the bouncer views from showing when passive auth may be used for device entry. --> <integer name="primary_bouncer_passive_auth_delay">500</integer> + + <!-- + The package name of the app store app. If empty, features using this should be gracefully + disabled. + --> + <string name="config_appStorePackageName" translatable="false"></string> + + <!-- Template for a link that leads to an app page in the relevant app store. If empty, + features using this should be gracefully disabled. If not empty, it must include a + "$packageName" part that will be replaced by the code with the package name of the target app. + --> + <string name="config_appStoreAppLinkTemplate" translatable="false"></string> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8d3ba364da06..4f768cc39b40 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -572,6 +572,7 @@ <dimen name="qs_brightness_margin_bottom">16dp</dimen> <dimen name="qqs_layout_margin_top">16dp</dimen> <dimen name="qqs_layout_padding_bottom">24dp</dimen> + <item name="qqs_expand_clock_scale" format="float" type="dimen">2.57</item> <!-- Most of the time it should be the same as notification_side_paddings as it's vertically aligned with notifications. The exception is split shade when this value becomes diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b48296fe54be..663efea1944b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2724,8 +2724,8 @@ <string name="media_output_broadcast_last_update_error">Can\u2019t save.</string> <!-- The hint message when Broadcast code is less than 4 characters [CHAR LIMIT=60] --> <string name="media_output_broadcast_code_hint_no_less_than_min">Use at least 4 characters</string> - <!-- The hint message when Broadcast code is more than 16 characters [CHAR LIMIT=60] --> - <string name="media_output_broadcast_code_hint_no_more_than_max">Use fewer than 16 characters</string> + <!-- The hint message when Broadcast edit is more than 16/254 characters [CHAR LIMIT=60] --> + <string name="media_output_broadcast_edit_hint_no_more_than_max">Use fewer than <xliff:g id="length" example="16">%1$d</xliff:g> characters</string> <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]--> <string name="build_number_clip_data_label">Build number</string> @@ -3016,52 +3016,54 @@ --> <string name="keyguard_affordance_enablement_dialog_action_template">Open <xliff:g id="appName" example="Wallet">%1$s</xliff:g></string> - <!-- - Requirement for the wallet app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + <!--- + Explains that the wallet app is not available because it is not installed. This is shown as part + of a dialog that explains to the user why they cannot select this shortcut for their lock screen + right now. + [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1">• The app is set up</string> + <string name="wallet_quick_affordance_unavailable_install_the_app">To add the Wallet app as a shortcut, make sure the app is installed</string> - <!-- - Requirement for the wallet app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + <!--- + Explains that the wallet app is not available because it is not installed. This is shown as part + of a dialog that explains to the user why they cannot select this shortcut for their lock screen + right now. + [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2">• At least one card has been added to Wallet</string> + <string name="wallet_quick_affordance_unavailable_configure_the_app">To add the Wallet app as a shortcut, make sure at least one card has been added</string> <!-- Requirement for the QR code scanner functionality to be available for the user to use. This is shown as part of a bulleted list of requirements. When all requirements are met, the piece of functionality can be accessed through a shortcut button on the lock screen. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction">• Install a camera app</string> + <string name="qr_scanner_quick_affordance_unavailable_explanation">To add the QR code scanner as a shortcut, make sure a camera app is installed</string> <!-- - Requirement for the home app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + Explains that the lock screen shortcut for the "home" app is not available because the app isn't + installed. This is shown as part of a dialog that explains to the user why they cannot select + this shortcut for their lock screen right now. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_home_instruction_1">• The app is set up</string> + <string name="home_quick_affordance_unavailable_install_the_app">To add the Home app as a shortcut, make sure the app is installed</string> <!-- - Requirement for the home app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + Explains that the lock screen shortcut for the "home" app is not available because the app isn't + configured. This is shown as part of a dialog that explains to the user why they cannot select + this shortcut for their lock screen right now. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string> + <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device is available</string> <!--- - Requirement for the notes app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE] --> - <string name="keyguard_affordance_enablement_dialog_notes_app_instruction">Select a default notes app to use the notetaking shortcut</string> + Explains that the notes app is not available. This is shown as part of a dialog that explains to + the user why they cannot select this shortcut for their lock screen right now. + [CHAR LIMIT=NONE]. + --> + <string name="notes_app_quick_affordance_unavailable_explanation">Select a default notes app to use the notetaking shortcut</string> <!--- The action to make the lock screen shortcut for the notes app to be available for the user to - use. This is shown as the action button in the dialog listing the requirements. When all - requirements are met, the app can be accessed through a shortcut button on the lock screen. - [CHAR LIMIT=NONE] --> + use. This is shown as the action button in the dialog explaining why the shortcut isn't + available. [CHAR LIMIT=NONE] --> <string name="keyguard_affordance_enablement_dialog_notes_app_action">Select app</string> <!-- @@ -3153,4 +3155,11 @@ <!--- Content of toast triggered when the notes app entry point is triggered without setting a default notes app. [CHAR LIMIT=NONE] --> <string name="set_default_notes_app_toast_content">Set default notes app in Settings</string> + + <!-- + Label for a button that, when clicked, sends the user to the app store to install an app. + + [CHAR LIMIT=64]. + --> + <string name="install_app">Install app</string> </resources> diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml index 52a98984e6e2..8039c68485ca 100644 --- a/packages/SystemUI/res/xml/qs_header.xml +++ b/packages/SystemUI/res/xml/qs_header.xml @@ -43,8 +43,8 @@ app:layout_constraintBottom_toBottomOf="@id/carrier_group" /> <Transform - android:scaleX="2.57" - android:scaleY="2.57" + android:scaleX="@dimen/qqs_expand_clock_scale" + android:scaleY="@dimen/qqs_expand_clock_scale" /> </Constraint> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index c5979cc50c3c..7a8c82cee32a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -65,8 +65,8 @@ class UnfoldConstantTranslateAnimator( } else { 1 } - viewsToTranslate.forEach { (view, direction) -> - view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier + viewsToTranslate.forEach { (view, direction, func) -> + view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) } } } @@ -77,7 +77,7 @@ class UnfoldConstantTranslateAnimator( .filter { it.shouldBeAnimated() } .mapNotNull { parent.findViewById<View>(it.viewId)?.let { view -> - ViewToTranslate(WeakReference(view), it.direction) + ViewToTranslate(WeakReference(view), it.direction, it.translateFunc) } } .toList() @@ -91,14 +91,19 @@ class UnfoldConstantTranslateAnimator( data class ViewIdToTranslate( val viewId: Int, val direction: Direction, - val shouldBeAnimated: () -> Boolean = { true } + val shouldBeAnimated: () -> Boolean = { true }, + val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value }, ) /** * Represents a view whose animation process is in-progress. It should be immutable because the * started animation should be completed. */ - private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction) + private data class ViewToTranslate( + val view: WeakReference<View>, + val direction: Direction, + val translateFunc: (View, Float) -> Unit, + ) /** Direction of the animation. */ enum class Direction(val multiplier: Float) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 9a9a2426507f..6b67c092890d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -79,17 +79,26 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin has been created. */ public void onCreate() { - mListener.onPluginAttached(this); + boolean loadPlugin = mListener.onPluginAttached(this); + if (!loadPlugin) { + if (mPlugin != null) { + unloadPlugin(); + } + return; + } + if (mPlugin == null) { loadPlugin(); - } else { - if (!(mPlugin instanceof PluginFragment)) { - // Only call onCreate for plugins that aren't fragments, as fragments - // will get the onCreate as part of the fragment lifecycle. - mPlugin.onCreate(mAppContext, mPluginContext); - } - mListener.onPluginLoaded(mPlugin, mPluginContext, this); + return; } + + mPluginFactory.checkVersion(mPlugin); + if (!(mPlugin instanceof PluginFragment)) { + // Only call onCreate for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + mPlugin.onCreate(mAppContext, mPluginContext); + } + mListener.onPluginLoaded(mPlugin, mPluginContext, this); } /** Alerts listener and plugin that the plugin is being shutdown. */ @@ -118,6 +127,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } + mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. @@ -205,12 +215,8 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager PluginFactory<T> pluginFactory = new PluginFactory<T>( context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass, () -> getClassLoader(appInfo, mBaseClassLoader)); - // TODO: Only create the plugin before version check if we need it for - // legacy version check. - T instance = pluginFactory.createPlugin(); - pluginFactory.checkVersion(instance); return new PluginInstance<T>( - context, listener, componentName, pluginFactory, instance); + context, listener, componentName, pluginFactory, null); } private boolean isPluginPackagePrivileged(String packageName) { @@ -332,7 +338,9 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager ClassLoader loader = mClassLoaderFactory.get(); Class<T> instanceClass = (Class<T>) Class.forName( mComponentName.getClassName(), true, loader); - return (T) mInstanceFactory.create(instanceClass); + T result = (T) mInstanceFactory.create(instanceClass); + Log.v(TAG, "Created plugin: " + result); + return result; } catch (ClassNotFoundException ex) { Log.e(TAG, "Failed to load plugin", ex); } catch (IllegalAccessException ex) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index d9d64ad5a893..376e27c6cf44 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,8 +1,14 @@ package com.android.keyguard; import static android.view.View.ALPHA; +import static android.view.View.SCALE_X; +import static android.view.View.SCALE_Y; import static android.view.View.TRANSLATION_Y; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -17,6 +23,7 @@ import android.widget.RelativeLayout; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; +import androidx.core.content.res.ResourcesCompat; import com.android.app.animation.Interpolators; import com.android.keyguard.dagger.KeyguardStatusViewScope; @@ -44,6 +51,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final long STATUS_AREA_START_DELAY_MILLIS = 0; private static final long STATUS_AREA_MOVE_UP_MILLIS = 967; private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467; + private static final float SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER = 1.4f; @IntDef({LARGE, SMALL}) @Retention(RetentionPolicy.SOURCE) @@ -88,14 +96,18 @@ public class KeyguardClockSwitch extends RelativeLayout { private KeyguardClockFrame mLargeClockFrame; private ClockController mClock; - private View mStatusArea; + private KeyguardStatusAreaView mStatusArea; private int mSmartspaceTopOffset; + private float mWeatherClockSmartspaceScaling = 1f; + private int mWeatherClockSmartspaceTranslateX = 0; + private int mWeatherClockSmartspaceTranslateY = 0; private int mDrawAlpha = 255; /** * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + private boolean mSplitShadeCentered = false; /** * Indicates which clock is currently displayed - should be one of {@link ClockSize}. @@ -105,7 +117,7 @@ public class KeyguardClockSwitch extends RelativeLayout { @VisibleForTesting AnimatorSet mClockInAnim = null; @VisibleForTesting AnimatorSet mClockOutAnim = null; - private AnimatorSet mStatusAreaAnim = null; + @VisibleForTesting AnimatorSet mStatusAreaAnim = null; private int mClockSwitchYAmount; @VisibleForTesting boolean mChildrenAreLaidOut = false; @@ -117,13 +129,30 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { + public void onConfigChanged() { mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_clock_switch_y_shift); mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset); + mWeatherClockSmartspaceScaling = ResourcesCompat.getFloat( + mContext.getResources(), R.dimen.weather_clock_smartspace_scale); + mWeatherClockSmartspaceTranslateX = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateX); + mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateY); + updateStatusArea(/* animate= */false); + } + + /** + * Enable or disable split shade specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + if (mSplitShadeCentered != splitShadeCentered) { + mSplitShadeCentered = splitShadeCentered; + updateStatusArea(/* animate= */true); + } } @Override @@ -134,7 +163,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large); mStatusArea = findViewById(R.id.keyguard_status_area); - onDensityOrFontScaleChanged(); + onConfigChanged(); } @Override @@ -182,6 +211,13 @@ public class KeyguardClockSwitch extends RelativeLayout { mSmallClockFrame.addView(clock.getSmallClock().getView()); mLargeClockFrame.addView(clock.getLargeClock().getView()); updateClockTargetRegions(); + updateStatusArea(/* animate= */false); + } + + private void updateStatusArea(boolean animate) { + if (mDisplayedClockSize != null && mChildrenAreLaidOut) { + updateClockViews(mDisplayedClockSize == LARGE, animate); + } } void updateClockTargetRegions() { @@ -230,13 +266,25 @@ public class KeyguardClockSwitch extends RelativeLayout { mStatusAreaAnim = null; View in, out; - float statusAreaYTranslation, clockInYTranslation, clockOutYTranslation; + float statusAreaYTranslation, statusAreaClockScale = 1f; + float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f; + float clockInYTranslation, clockOutYTranslation; if (useLargeClock) { out = mSmallClockFrame; in = mLargeClockFrame; if (indexOfChild(in) == -1) addView(in, 0); statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop() + mSmartspaceTopOffset; + // TODO: Load from clock config when less risky + if (mClock != null + && mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) { + statusAreaClockScale = mWeatherClockSmartspaceScaling; + statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX; + statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY; + if (mSplitShadeCentered) { + statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER; + } + } clockInYTranslation = 0; clockOutYTranslation = 0; // Small clock translation is handled with statusArea } else { @@ -258,7 +306,12 @@ public class KeyguardClockSwitch extends RelativeLayout { in.setAlpha(1f); in.setTranslationY(clockInYTranslation); in.setVisibility(View.VISIBLE); - mStatusArea.setTranslationY(statusAreaYTranslation); + mStatusArea.setScaleX(statusAreaClockScale); + mStatusArea.setScaleY(statusAreaClockScale); + mStatusArea.setTranslateXFromClockDesign(statusAreaClockTranslateX); + mStatusArea.setTranslateYFromClockDesign(statusAreaClockTranslateY); + mStatusArea.setTranslateYFromClockSize(statusAreaYTranslation); + mSmallClockFrame.setTranslationY(statusAreaYTranslation); return; } @@ -295,8 +348,15 @@ public class KeyguardClockSwitch extends RelativeLayout { useLargeClock ? STATUS_AREA_MOVE_UP_MILLIS : STATUS_AREA_MOVE_DOWN_MILLIS); mStatusAreaAnim.setInterpolator(Interpolators.EMPHASIZED); mStatusAreaAnim.playTogether( - ObjectAnimator.ofFloat(mStatusArea, TRANSLATION_Y, statusAreaYTranslation), - ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation)); + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_SIZE.getProperty(), + statusAreaYTranslation), + ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation), + ObjectAnimator.ofFloat(mStatusArea, SCALE_X, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, SCALE_Y, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_X_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateX), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateY)); mStatusAreaAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { mStatusAreaAnim = null; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 99e25745dda7..41c1eda42e83 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -330,10 +330,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { - mView.onDensityOrFontScaleChanged(); + public void onConfigChanged() { + mView.onConfigChanged(); mKeyguardSmallClockTopMargin = mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); mKeyguardLargeClockTopMargin = @@ -344,6 +344,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS setDateWeatherVisibility(); } + /** + * Enable or disable split shade center specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + mView.setSplitShadeCentered(splitShadeCentered); + } /** * Set which clock should be displayed on the keyguard. The other one will be automatically @@ -407,7 +413,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS scale, props, animate); if (mStatusArea != null) { - PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X, + PropertyAnimator.setProperty(mStatusArea, KeyguardStatusAreaView.TRANSLATE_X_AOD, x, props, animate); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt new file mode 100644 index 000000000000..e7da2b977379 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt @@ -0,0 +1,118 @@ +package com.android.keyguard + +import android.content.Context +import android.util.AttributeSet +import android.util.FloatProperty +import android.widget.LinearLayout +import com.android.systemui.R +import com.android.systemui.statusbar.notification.AnimatableProperty + +class KeyguardStatusAreaView( + context: Context, + attrs: AttributeSet? = null, +) : LinearLayout(context, attrs) { + var translateXFromClockDesign = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromAod = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromUnfold = 0F + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateYFromClockSize = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockDesign + } + + var translateYFromClockDesign = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockSize + } + + companion object { + @JvmField + val TRANSLATE_X_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromClockDesign + } + }, + R.id.translate_x_clock_design_animator_tag, + R.id.translate_x_clock_design_animator_start_tag, + R.id.translate_x_clock_design_animator_end_tag + ) + + @JvmField + val TRANSLATE_X_AOD = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXAod") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromAod = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromAod + } + }, + R.id.translate_x_aod_animator_tag, + R.id.translate_x_aod_animator_start_tag, + R.id.translate_x_aod_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_SIZE = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockSize") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockSize = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockSize + } + }, + R.id.translate_y_clock_size_animator_tag, + R.id.translate_y_clock_size_animator_start_tag, + R.id.translate_y_clock_size_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockDesign + } + }, + R.id.translate_y_clock_design_animator_tag, + R.id.translate_y_clock_design_animator_start_tag, + R.id.translate_y_clock_design_animator_end_tag + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 835cc13bebb1..00500d617766 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.content.res.Configuration; import android.graphics.Rect; import android.transition.ChangeBounds; import android.transition.Transition; @@ -280,8 +281,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } @Override - public void onDensityOrFontScaleChanged() { - mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); + public void onConfigChanged(Configuration newConfig) { + mKeyguardClockSwitchController.onConfigChanged(); } }; @@ -329,6 +330,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { + mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered); if (mStatusViewCentered == shouldBeCentered) { return; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index edd150c293f6..ca64ae059093 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -53,7 +53,10 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard), + ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, + { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + }), ViewIdToTranslate( R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 1721891550a1..2bf8fb36469c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3565,7 +3565,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private void handleTimeUpdate() { Assert.isMainThread(); - mLogger.d("handleTimeUpdate"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3630,9 +3629,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void handleBatteryUpdate(BatteryStatus status) { Assert.isMainThread(); final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status); - mLogger.logHandleBatteryUpdate(batteryUpdateInteresting); mBatteryStatus = status; if (batteryUpdateInteresting) { + mLogger.logHandleBatteryUpdate(mBatteryStatus); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3669,7 +3668,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.logSimState(subId, slotId, state); boolean becameAbsent = false; - if (!SubscriptionManager.isValidSubscriptionId(subId)) { + if (!SubscriptionManager.isValidSubscriptionId(subId) + && state != TelephonyManager.SIM_STATE_UNKNOWN) { mLogger.w("invalid subId in handleSimStateChange()"); /* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to * handleServiceStateChange() handle other case */ @@ -3704,7 +3704,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab data.subId = subId; data.slotId = slotId; } - if ((changed || becameAbsent) && state != TelephonyManager.SIM_STATE_UNKNOWN) { + if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java index 4557b3418e19..500910c910c3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java @@ -30,6 +30,7 @@ import android.transition.TransitionManager; import android.transition.TransitionValues; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -51,12 +52,30 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu private int mPosition = 0; private final PinShapeAdapter mPinShapeAdapter; private ValueAnimator mValueAnimator = ValueAnimator.ofFloat(1f, 0f); + private Rect mFirstChildVisibleRect = new Rect(); public PinShapeNonHintingView(Context context, AttributeSet attrs) { super(context, attrs); mPinShapeAdapter = new PinShapeAdapter(context); } @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (getChildCount() > 0) { + View firstChild = getChildAt(0); + boolean isVisible = firstChild.getLocalVisibleRect(mFirstChildVisibleRect); + boolean clipped = mFirstChildVisibleRect.left > 0 + || mFirstChildVisibleRect.right < firstChild.getWidth(); + if (!isVisible || clipped) { + setGravity(Gravity.END | Gravity.CENTER_VERTICAL); + return; + } + } + + setGravity(Gravity.CENTER); + } + + @Override public void append() { int size = getResources().getDimensionPixelSize(R.dimen.password_shape_size); ImageView pinDot = new ImageView(getContext()); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index 4923ab0fab18..b5963312cb2d 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -30,7 +30,7 @@ import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.KeyguardListenModel import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.TrustGrantFlags -import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog +import com.android.settingslib.fuelgauge.BatteryStatus import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel import com.android.systemui.log.LogLevel.DEBUG @@ -38,6 +38,7 @@ import com.android.systemui.log.LogLevel.ERROR import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.LogLevel.VERBOSE import com.android.systemui.log.LogLevel.WARNING +import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -683,8 +684,27 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logHandleBatteryUpdate(isInteresting: Boolean) { - logBuffer.log(TAG, DEBUG, { bool1 = isInteresting }, { "handleBatteryUpdate: $bool1" }) + fun logHandleBatteryUpdate(batteryStatus: BatteryStatus?) { + logBuffer.log( + TAG, + DEBUG, + { + bool1 = batteryStatus != null + int1 = batteryStatus?.status ?: -1 + int2 = batteryStatus?.chargingStatus ?: -1 + long1 = (batteryStatus?.level ?: -1).toLong() + long2 = (batteryStatus?.maxChargingWattage ?: -1).toLong() + str1 = "${batteryStatus?.plugged ?: -1}" + }, + { + "handleBatteryUpdate: isNotNull: $bool1 " + + "BatteryStatus{status= $int1, " + + "level=$long1, " + + "plugged=$str1, " + + "chargingStatus=$int2, " + + "maxChargingWattage= $long2}" + } + ) } fun scheduleWatchdog(@CompileTimeConstant watchdogType: String) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index f86e2ed6eb91..7f706859abb3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -614,7 +614,11 @@ public class AuthContainerView extends LinearLayout return ((AuthBiometricFingerprintView) view).isUdfps(); } if (view instanceof BiometricPromptLayout) { - return ((BiometricPromptLayout) view).isUdfps(); + // this will force the prompt to align itself on the edge of the screen + // instead of centering (temporary workaround to prevent small implicit view + // from breaking due to the way gravity / margins are set in the legacy + // AuthPanelController + return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index baaa96efb5f0..d48b9c339d15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -35,6 +35,7 @@ import android.os.Handler import android.util.Log import android.util.RotationUtils import android.view.Display +import android.view.DisplayInfo import android.view.Gravity import android.view.LayoutInflater import android.view.Surface @@ -58,6 +59,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.util.boundsOnScreen import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.traceSection import java.io.PrintWriter @@ -129,6 +131,8 @@ constructor( } @VisibleForTesting var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT + private val displayInfo = DisplayInfo() + private val overlayViewParams = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, @@ -214,6 +218,23 @@ constructor( for (requestSource in requests) { pw.println(" $requestSource.name") } + + pw.println("overlayView:") + pw.println(" width=${overlayView?.width}") + pw.println(" height=${overlayView?.height}") + pw.println(" boundsOnScreen=${overlayView?.boundsOnScreen}") + + pw.println("displayStateInteractor:") + pw.println(" isInRearDisplayMode=${displayStateInteractor?.isInRearDisplayMode?.value}") + + pw.println("sensorProps:") + pw.println(" displayId=${displayInfo.uniqueId}") + pw.println(" sensorType=${sensorProps?.sensorType}") + pw.println(" location=${sensorProps?.getLocation(displayInfo.uniqueId)}") + + pw.println("overlayOffsets=$overlayOffsets") + pw.println("isReverseDefaultRotation=$isReverseDefaultRotation") + pw.println("currentRotation=${displayInfo.rotation}") } private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) { @@ -226,6 +247,8 @@ constructor( val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) overlayView = view val display = context.display!! + // b/284098873 `context.display.rotation` may not up-to-date, we use displayInfo.rotation + display.getDisplayInfo(displayInfo) val offsets = sensorProps.getLocation(display.uniqueId).let { location -> if (location == null) { @@ -239,12 +262,12 @@ constructor( view.rotation = display.asSideFpsAnimationRotation( offsets.isYAligned(), - getRotationFromDefault(display.rotation) + getRotationFromDefault(displayInfo.rotation) ) lottie.setAnimation( display.asSideFpsAnimation( offsets.isYAligned(), - getRotationFromDefault(display.rotation) + getRotationFromDefault(displayInfo.rotation) ) ) lottie.addLottieOnCompositionLoadedListener { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java index a2840fc1dc8c..056d692a3641 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java @@ -103,6 +103,13 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { } @Override + void onSensorRectUpdated(RectF bounds) { + super.onSensorRectUpdated(bounds); + bounds.round(this.mSensorBounds); + postInvalidate(); + } + + @Override void onDisplayConfiguring() { } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index e4c4e9aedb56..1dffa80a084f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -19,8 +19,11 @@ package com.android.systemui.biometrics.ui.binder import android.animation.Animator import android.animation.AnimatorSet import android.animation.ValueAnimator +import android.view.Surface import android.view.View import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.widget.TextView import androidx.core.animation.addListener @@ -52,7 +55,9 @@ object BiometricViewSizeBinder { panelViewController: AuthPanelController, jankListener: BiometricJankListener, ) { - val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!! + val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java)) + val accessibilityManager = + requireNotNull(view.context.getSystemService(AccessibilityManager::class.java)) fun notifyAccessibilityChanged() { Utils.notifyAccessibilityContentChanged(accessibilityManager, view) } @@ -102,15 +107,26 @@ object BiometricViewSizeBinder { when { size.isSmall -> { iconHolderView.alpha = 1f + val bottomInset = + windowManager.maximumWindowMetrics.windowInsets + .getInsets(WindowInsets.Type.navigationBars()) + .bottom iconHolderView.y = - view.height - iconHolderView.height - iconPadding + if (view.isLandscape()) { + (view.height - iconHolderView.height - bottomInset) / 2f + } else { + view.height - + iconHolderView.height - + iconPadding - + bottomInset + } val newHeight = - iconHolderView.height + 2 * iconPadding.toInt() - + iconHolderView.height + (2 * iconPadding.toInt()) - iconHolderView.paddingTop - iconHolderView.paddingBottom panelViewController.updateForContentDimensions( width, - newHeight, + newHeight + bottomInset, 0, /* animateDurationMs */ ) } @@ -181,6 +197,11 @@ object BiometricViewSizeBinder { } } +private fun View.isLandscape(): Boolean { + val r = context.display.rotation + return r == Surface.ROTATION_90 || r == Surface.ROTATION_270 +} + private fun TextView.showTextOrHide(forceHide: Boolean = false) { visibility = if (forceHide || text.isBlank()) View.GONE else View.VISIBLE } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt index d7d17006168e..c92180647ab3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.controller interface ControlsTileResourceConfiguration { + fun getPackageName(): String? fun getTileTitleId(): Int fun getTileImageId(): Int -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt index c96d3d4a2602..02490605fbb8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt @@ -20,12 +20,14 @@ import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -/** - * Default Instance for ControlsTileResourceConfiguration. - */ +/** Default Instance for ControlsTileResourceConfiguration. */ @SysUISingleton -class ControlsTileResourceConfigurationImpl @Inject constructor() - : ControlsTileResourceConfiguration { +class ControlsTileResourceConfigurationImpl @Inject constructor() : + ControlsTileResourceConfiguration { + override fun getPackageName(): String? { + return null + } + override fun getTileTitleId(): Int { return R.string.quick_controls_title } @@ -33,4 +35,4 @@ class ControlsTileResourceConfigurationImpl @Inject constructor() override fun getTileImageId(): Int { return R.drawable.controls_icon } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 7509a8ad0c88..94e563319524 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -29,9 +29,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy -import kotlinx.coroutines.flow.StateFlow import java.util.Optional import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow /** * Pseudo-component to inject into classes outside `com.android.systemui.controls`. @@ -40,26 +40,26 @@ import javax.inject.Inject * instantiated if `featureEnabled` is true. Can also be queried for the availability of controls. */ @SysUISingleton -class ControlsComponent @Inject constructor( - @ControlsFeatureEnabled private val featureEnabled: Boolean, - private val context: Context, - private val lazyControlsController: Lazy<ControlsController>, - private val lazyControlsUiController: Lazy<ControlsUiController>, - private val lazyControlsListingController: Lazy<ControlsListingController>, - private val lockPatternUtils: LockPatternUtils, - private val keyguardStateController: KeyguardStateController, - private val userTracker: UserTracker, - controlsSettingsRepository: ControlsSettingsRepository, - optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration> +class ControlsComponent +@Inject +constructor( + @ControlsFeatureEnabled private val featureEnabled: Boolean, + private val context: Context, + private val lazyControlsController: Lazy<ControlsController>, + private val lazyControlsUiController: Lazy<ControlsUiController>, + private val lazyControlsListingController: Lazy<ControlsListingController>, + private val lockPatternUtils: LockPatternUtils, + private val keyguardStateController: KeyguardStateController, + private val userTracker: UserTracker, + controlsSettingsRepository: ControlsSettingsRepository, + optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration> ) { val canShowWhileLockedSetting: StateFlow<Boolean> = - controlsSettingsRepository.canShowControlsInLockscreen + controlsSettingsRepository.canShowControlsInLockscreen private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration = - optionalControlsTileResourceConfiguration.orElse( - ControlsTileResourceConfigurationImpl() - ) + optionalControlsTileResourceConfiguration.orElse(ControlsTileResourceConfigurationImpl()) fun getControlsController(): Optional<ControlsController> { return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty() @@ -77,9 +77,7 @@ class ControlsComponent @Inject constructor( } } - /** - * @return true if controls are feature-enabled and the user has the setting enabled - */ + /** @return true if controls are feature-enabled and the user has the setting enabled */ fun isEnabled() = featureEnabled /** @@ -90,8 +88,10 @@ class ControlsComponent @Inject constructor( */ fun getVisibility(): Visibility { if (!isEnabled()) return Visibility.UNAVAILABLE - if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) - == STRONG_AUTH_REQUIRED_AFTER_BOOT) { + if ( + lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) == + STRONG_AUTH_REQUIRED_AFTER_BOOT + ) { return Visibility.AVAILABLE_AFTER_UNLOCK } if (!canShowWhileLockedSetting.value && !keyguardStateController.isUnlocked()) { @@ -102,7 +102,13 @@ class ControlsComponent @Inject constructor( } enum class Visibility { - AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE + AVAILABLE, + AVAILABLE_AFTER_UNLOCK, + UNAVAILABLE + } + + fun getPackageName(): String? { + return controlsTileResourceConfiguration.getPackageName() } fun getTileTitleId(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index e5fa2090bf0f..1fbbe1cce480 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -601,7 +601,7 @@ object Flags { // 1300 - screenshots // TODO(b/264916608): Tracking Bug - @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata", teamfood = true) + @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata") // TODO(b/266955521): Tracking bug @JvmField val SCREENSHOT_DETECTION = releasedFlag(1303, "screenshot_detection") @@ -728,6 +728,13 @@ object Flags { @JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter") + // 2900 - Zero Jank fixes. Naming convention is: zj_<bug number>_<cuj name> + + // TODO:(b/285623104): Tracking bug + @JvmField + val ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD = + releasedFlag(2900, "zj_285570694_lockscreen_transition_from_aod") + // TODO(b/283084712): Tracking Bug @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 27a5974e6299..bc0713952ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard import android.content.ContentProvider import android.content.ContentValues import android.content.Context +import android.content.Intent import android.content.UriMatcher import android.content.pm.PackageManager import android.content.pm.ProviderInfo @@ -282,11 +283,11 @@ class CustomizationProvider : Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON, Contract.LockScreenQuickAffordances.AffordanceTable.Columns.IS_ENABLED, Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_INSTRUCTIONS, + .ENABLEMENT_EXPLANATION, Contract.LockScreenQuickAffordances.AffordanceTable.Columns .ENABLEMENT_ACTION_TEXT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_COMPONENT_NAME, + .ENABLEMENT_ACTION_INTENT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns.CONFIGURE_INTENT, ) ) @@ -298,13 +299,10 @@ class CustomizationProvider : representation.name, representation.iconResourceId, if (representation.isEnabled) 1 else 0, - representation.instructions?.joinToString( - Contract.LockScreenQuickAffordances.AffordanceTable - .ENABLEMENT_INSTRUCTIONS_DELIMITER - ), + representation.explanation, representation.actionText, - representation.actionComponentName, - representation.configureIntent?.toUri(0), + representation.actionIntent?.toUri(Intent.URI_INTENT_SCHEME), + representation.configureIntent?.toUri(Intent.URI_INTENT_SCHEME), ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 6948c8d4e563..0e0cf74ba463 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -697,6 +697,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } break; + case TelephonyManager.SIM_STATE_UNKNOWN: + mPendingPinLock = false; + break; default: if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState); break; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index abb63c4d34ce..f3fc8096f5d7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.annotation.DrawableRes @@ -34,6 +35,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.appStoreIntent import com.android.systemui.util.kotlin.getOrNull import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -76,18 +78,44 @@ constructor( component.getControlsListingController().getOrNull()?.getCurrentServices() val hasFavorites = component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true - if (currentServices.isNullOrEmpty() || !hasFavorites) { - return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( + val componentPackageName = component.getPackageName() + when { + currentServices.isNullOrEmpty() && !componentPackageName.isNullOrEmpty() -> { + // No home app installed but we know which app we want to install. + return disabledPickerState( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_1 + R.string.home_quick_affordance_unavailable_install_the_app ), + actionText = context.getString(R.string.install_app), + actionIntent = appStoreIntent(context, componentPackageName), + ) + } + currentServices.isNullOrEmpty() && componentPackageName.isNullOrEmpty() -> { + // No home app installed and we don't know which app we want to install. + return disabledPickerState( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_2 + R.string.home_quick_affordance_unavailable_install_the_app ), - ), - ) + ) + } + !hasFavorites -> { + // Home app installed but no favorites selected. + val activityClass = component.getControlsUiController().get().resolveActivity() + return disabledPickerState( + explanation = + context.getString( + R.string.home_quick_affordance_unavailable_configure_the_app + ), + actionText = context.getString(R.string.controls_open_app), + actionIntent = + Intent().apply { + component = ComponentName(context, activityClass) + putExtra(ControlsUiController.EXTRA_ANIMATE, true) + }, + ) + } } return KeyguardQuickAffordanceConfig.PickerScreenState.Default() @@ -172,6 +200,20 @@ constructor( } } + private fun disabledPickerState( + explanation: String, + actionText: String? = null, + actionIntent: Intent? = null, + ): KeyguardQuickAffordanceConfig.PickerScreenState.Disabled { + check(actionIntent == null || actionText != null) + + return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + explanation = explanation, + actionText = actionText, + actionIntent = actionIntent, + ) + } + companion object { private const val TAG = "HomeControlsKeyguardQuickAffordanceConfig" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt index 28dc5bdcc45f..320d158fb13e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt @@ -18,11 +18,13 @@ package com.android.systemui.keyguard.data.quickaffordance import android.app.AlertDialog +import android.content.Context import android.content.Intent +import android.net.Uri +import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import kotlinx.coroutines.flow.Flow /** Defines interface that can act as data source for a single quick affordance model. */ @@ -80,37 +82,35 @@ interface KeyguardQuickAffordanceConfig { /** * The picker shows the item for selecting this affordance as disabled. Clicking on it will - * show the given instructions to the user. If [actionText] and [actionComponentName] are - * provided (optional) a button will be shown to open an activity to help the user complete - * the steps described in the instructions. + * show the given instructions to the user. If [actionText] and [actionIntent] are provided + * (optional) a button will be shown to open an activity to help the user complete the steps + * described in the instructions. */ data class Disabled( - /** List of human-readable instructions for setting up the quick affordance. */ - val instructions: List<String>, + /** Human-readable explanation as to why the quick affordance is current disabled. */ + val explanation: String, /** * Optional text to display on a button that the user can click to start a flow to go * and set up the quick affordance and make it enabled. */ val actionText: String? = null, /** - * Optional component name to be able to build an `Intent` that opens an `Activity` for - * the user to be able to set up the quick affordance and make it enabled. - * - * This is either just an action for the `Intent` or a package name and action, - * separated by - * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for - * convenience, you can use the [componentName] function. + * Optional [Intent] that opens an `Activity` for the user to be able to set up the + * quick affordance and make it enabled. */ - val actionComponentName: String? = null, + val actionIntent: Intent? = null, ) : PickerScreenState() { init { - check(instructions.isNotEmpty()) { "Instructions must not be empty!" } + check(explanation.isNotEmpty()) { "Explanation must not be empty!" } check( - (actionText.isNullOrEmpty() && actionComponentName.isNullOrEmpty()) || - (!actionText.isNullOrEmpty() && !actionComponentName.isNullOrEmpty()) + (actionText.isNullOrEmpty() && actionIntent == null) || + (!actionText.isNullOrEmpty() && actionIntent != null) ) { - "actionText and actionComponentName must either both be null/empty or both be" + - " non-empty!" + """ + actionText and actionIntent must either both be null/empty or both be + non-null and non-empty! + """ + .trimIndent() } } } @@ -163,17 +163,33 @@ interface KeyguardQuickAffordanceConfig { } companion object { - fun componentName( - packageName: String? = null, - action: String?, - ): String? { - return when { - action.isNullOrEmpty() -> null - !packageName.isNullOrEmpty() -> - "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable - .COMPONENT_NAME_SEPARATOR}$action" - else -> action + + /** + * Returns an [Intent] that can be used to start an activity that opens the app store app to + * a page showing the app with the passed-in [packageName]. + * + * If the feature isn't enabled on this device/variant/configuration, a `null` will be + * returned. + */ + fun appStoreIntent(context: Context, packageName: String?): Intent? { + if (packageName.isNullOrEmpty()) { + return null + } + + val appStorePackageName = context.getString(R.string.config_appStorePackageName) + val linkTemplate = context.getString(R.string.config_appStoreAppLinkTemplate) + if (appStorePackageName.isEmpty() || linkTemplate.isEmpty()) { + return null + } + + check(linkTemplate.contains(APP_PACKAGE_NAME_PLACEHOLDER)) + + return Intent(Intent.ACTION_VIEW).apply { + setPackage(appStorePackageName) + data = Uri.parse(linkTemplate.replace(APP_PACKAGE_NAME_PLACEHOLDER, packageName)) } } + + private const val APP_PACKAGE_NAME_PLACEHOLDER = "\$packageName" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt index 0d54ab9adb4a..20ed549e42d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt @@ -80,16 +80,14 @@ constructor( return when { !controller.isAvailableOnDevice -> KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice - !controller.isAbleToOpenCameraApp -> + !controller.isAbleToOpenCameraApp -> { KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( - context.getString( - R.string - .keyguard_affordance_enablement_dialog_qr_scanner_instruction - ), + explanation = + context.getString( + R.string.qr_scanner_quick_affordance_unavailable_explanation ), ) + } else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index 9db3c22dff84..c019d21e00ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -100,19 +100,20 @@ constructor( return when { !walletController.walletClient.isWalletServiceAvailable -> KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice - !isWalletAvailable() || queryCards().isEmpty() -> { + !isWalletAvailable() -> KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( - context.getString( - R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1 - ), - context.getString( - R.string.keyguard_affordance_enablement_dialog_wallet_instruction_2 - ), + explanation = + context.getString( + R.string.wallet_quick_affordance_unavailable_install_the_app + ), + ) + queryCards().isEmpty() -> + KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + explanation = + context.getString( + R.string.wallet_quick_affordance_unavailable_configure_the_app ), ) - } else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default() } } @@ -147,9 +148,7 @@ constructor( } private fun isWalletAvailable() = - with(walletController.walletClient) { - isWalletServiceAvailable && isWalletFeatureAvailable - } + with(walletController.walletClient) { isWalletServiceAvailable && isWalletFeatureAvailable } private fun state( isFeatureEnabled: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt index 96c94d7d64b6..34f6b4d74f2b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.content.Context +import android.content.Intent import android.os.UserHandle import android.util.LayoutDirection import com.android.systemui.Dumpable @@ -174,12 +175,23 @@ constructor( iconResourceId = config.pickerIconResourceId, isEnabled = pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.Default, - instructions = disabledPickerState?.instructions, + explanation = disabledPickerState?.explanation, actionText = disabledPickerState?.actionText, - actionComponentName = disabledPickerState?.actionComponentName, - configureIntent = defaultPickerState?.configureIntent, + actionIntent = + disabledPickerState?.actionIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, + configureIntent = + defaultPickerState?.configureIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, ) } + .sortedBy { it.name } } /** 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 22753376a5d3..ea9c2b28742c 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 @@ -35,6 +35,7 @@ import com.android.systemui.dock.retrieveIsDocked import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry @@ -77,6 +78,7 @@ constructor( private val logger: KeyguardQuickAffordancesMetricsLogger, private val devicePolicyManager: DevicePolicyManager, private val dockManager: DockManager, + private val biometricSettingsRepository: BiometricSettingsRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val appContext: Context, ) { @@ -107,9 +109,10 @@ constructor( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, keyguardInteractor.isKeyguardShowing, - keyguardInteractor.isQuickSettingsVisible - ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible -> - if (!isDozing && isKeyguardShowing && !isQuickSettingsVisible) { + keyguardInteractor.isQuickSettingsVisible, + biometricSettingsRepository.isCurrentUserInLockdown, + ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && !isQuickSettingsVisible && !isUserInLockdown) { affordance } else { KeyguardQuickAffordanceModel.Hidden diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt index e7e915940290..c6320dee030a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt @@ -32,8 +32,8 @@ data class KeyguardQuickAffordancePickerRepresentation( /** Whether this quick affordance is enabled. */ val isEnabled: Boolean = true, - /** If not enabled, the list of user-visible steps to re-enable it. */ - val instructions: List<String>? = null, + /** If not enabled, a user-visible explanation as to why. */ + val explanation: String? = null, /** * If not enabled, an optional label for a button that takes the user to a destination where @@ -41,11 +41,8 @@ data class KeyguardQuickAffordancePickerRepresentation( */ val actionText: String? = null, - /** - * If not enabled, an optional component name (package and action) for a button that takes the - * user to a destination where they can re-enable it. - */ - val actionComponentName: String? = null, + /** Optional [Intent] to use to start an activity to re-enable this affordance. */ + val actionIntent: Intent? = null, /** Optional [Intent] to use to start an activity to configure this affordance. */ val configureIntent: Intent? = null, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index c41f82b4ddd1..8d3c6d5d4947 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -38,8 +38,6 @@ import com.android.internal.app.ResolverListController import com.android.internal.app.chooser.NotSelectableTargetInfo import com.android.internal.app.chooser.TargetInfo import com.android.systemui.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler @@ -53,7 +51,6 @@ import javax.inject.Inject class MediaProjectionAppSelectorActivity( private val componentFactory: MediaProjectionAppSelectorComponent.Factory, private val activityLauncher: AsyncActivityLauncher, - private val featureFlags: FeatureFlags, /** This is used to override the dependency in a screenshot test */ @VisibleForTesting private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? @@ -62,9 +59,8 @@ class MediaProjectionAppSelectorActivity( @Inject constructor( componentFactory: MediaProjectionAppSelectorComponent.Factory, - activityLauncher: AsyncActivityLauncher, - featureFlags: FeatureFlags - ) : this(componentFactory, activityLauncher, featureFlags, listControllerFactory = null) + activityLauncher: AsyncActivityLauncher + ) : this(componentFactory, activityLauncher, listControllerFactory = null) private lateinit var configurationController: ConfigurationController private lateinit var controller: MediaProjectionAppSelectorController @@ -108,11 +104,7 @@ class MediaProjectionAppSelectorActivity( override fun appliedThemeResId(): Int = R.style.Theme_SystemUI_MediaProjectionAppSelector override fun createBlockerEmptyStateProvider(): EmptyStateProvider = - if (featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) { - component.emptyStateProvider - } else { - object : EmptyStateProvider {} - } + component.emptyStateProvider override fun createListController(userHandle: UserHandle): ResolverListController = listControllerFactory?.invoke(userHandle) ?: super.createListController(userHandle) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 516fbf5ca12c..0819d0d36e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -76,7 +76,6 @@ import androidx.constraintlayout.widget.ConstraintSet; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.InstanceId; import com.android.internal.widget.CachingIconView; @@ -249,9 +248,9 @@ public class MediaControlPanel { private final FeatureFlags mFeatureFlags; private final GlobalSettings mGlobalSettings; - // TODO(b/281032715): Consider making this as a final variable. For now having a null check - // due to unit test failure. (Perhaps missing some setup) private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; + private boolean mWasPlaying = false; + private boolean mButtonClicked = false; private ContentObserver mAnimationScaleObserver = new ContentObserver(null) { @Override @@ -583,6 +582,25 @@ public class MediaControlPanel { if (!mMetadataAnimationHandler.isRunning()) { mMediaViewController.refreshState(); } + + // Turbulence noise + if (shouldPlayTurbulenceNoise()) { + if (mTurbulenceNoiseAnimationConfig == null) { + mTurbulenceNoiseAnimationConfig = + createTurbulenceNoiseAnimation(); + } + // Color will be correctly updated in ColorSchemeTransition. + mTurbulenceNoiseController.play( + mTurbulenceNoiseAnimationConfig + ); + mMainExecutor.executeDelayed( + mTurbulenceNoiseController::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); + } + mButtonClicked = false; + mWasPlaying = isPlaying(); + Trace.endSection(); } @@ -1156,21 +1174,14 @@ public class MediaControlPanel { if (!mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) { mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); + // Used to determine whether to play turbulence noise. + mWasPlaying = isPlaying(); + mButtonClicked = true; + action.run(); + if (mFeatureFlags.isEnabled(Flags.UMO_SURFACE_RIPPLE)) { mMultiRippleController.play(createTouchRippleAnimation(button)); - if (mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE)) { - if (mTurbulenceNoiseAnimationConfig == null) { - mTurbulenceNoiseAnimationConfig = - createTurbulenceNoiseAnimation(); - } - // Color will be correctly updated in ColorSchemeTransition. - mTurbulenceNoiseController.play(mTurbulenceNoiseAnimationConfig); - mMainExecutor.executeDelayed( - mTurbulenceNoiseController::finish, - TURBULENCE_NOISE_PLAY_DURATION - ); - } } if (icon instanceof Animatable) { @@ -1209,26 +1220,31 @@ public class MediaControlPanel { ); } + private boolean shouldPlayTurbulenceNoise() { + return mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE) && mButtonClicked && !mWasPlaying + && isPlaying(); + } + private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() { return new TurbulenceNoiseAnimationConfig( - TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_GRID_COUNT, + /* gridCount= */ 2.14f, TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, - /* noiseMoveSpeedX= */ 0f, + /* noiseMoveSpeedX= */ 0.42f, /* noiseMoveSpeedY= */ 0f, TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), - // We want to add (BlendMode.PLUS) the turbulence noise on top of the album art. - // Thus, set the background color with alpha 0. - /* backgroundColor= */ ColorUtils.setAlphaComponent(Color.BLACK, 0), - TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY, - /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(), - /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(), + /* backgroundColor= */ Color.BLACK, + /* opacity= */ 51, + /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(), + /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(), TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, - /* easeInDuration= */ 2500f, - /* easeOutDuration= */ 2500f, - this.getContext().getResources().getDisplayMetrics().density, - BlendMode.PLUS, - /* onAnimationEnd= */ null + /* easeInDuration= */ 1350f, + /* easeOutDuration= */ 1350f, + getContext().getResources().getDisplayMetrics().density, + BlendMode.SCREEN, + /* onAnimationEnd= */ null, + /* lumaMatteBlendFactor= */ 0.26f, + /* lumaMatteOverallBrightness= */ 0.09f ); } private void clearButton(final ImageButton button) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java index 529b980a6e38..b4578e97eda2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java @@ -59,6 +59,17 @@ import com.google.zxing.WriterException; public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { private static final String TAG = "MediaOutputBroadcastDialog"; + static final int METADATA_BROADCAST_NAME = 0; + static final int METADATA_BROADCAST_CODE = 1; + + private static final int MAX_BROADCAST_INFO_UPDATE = 3; + @VisibleForTesting + static final int BROADCAST_CODE_MAX_LENGTH = 16; + @VisibleForTesting + static final int BROADCAST_CODE_MIN_LENGTH = 4; + @VisibleForTesting + static final int BROADCAST_NAME_MAX_LENGTH = 254; + private ViewStub mBroadcastInfoArea; private ImageView mBroadcastQrCodeView; private ImageView mBroadcastNotify; @@ -68,14 +79,16 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { private ImageView mBroadcastCodeEye; private Boolean mIsPasswordHide = true; private ImageView mBroadcastCodeEdit; - private AlertDialog mAlertDialog; + @VisibleForTesting + AlertDialog mAlertDialog; private TextView mBroadcastErrorMessage; private int mRetryCount = 0; private String mCurrentBroadcastName; private String mCurrentBroadcastCode; private boolean mIsStopbyUpdateBroadcastCode = false; + private boolean mIsLeBroadcastAssistantCallbackRegistered; - private TextWatcher mTextWatcher = new TextWatcher() { + private TextWatcher mBroadcastCodeTextWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // Do nothing @@ -103,7 +116,9 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { R.string.media_output_broadcast_code_hint_no_less_than_min); } else if (breakBroadcastCodeRuleTextLengthMoreThanMax) { mBroadcastErrorMessage.setText( - R.string.media_output_broadcast_code_hint_no_more_than_max); + mContext.getResources().getString( + R.string.media_output_broadcast_edit_hint_no_more_than_max, + BROADCAST_CODE_MAX_LENGTH)); } mBroadcastErrorMessage.setVisibility(breakRule ? View.VISIBLE : View.INVISIBLE); @@ -114,7 +129,40 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } }; - private boolean mIsLeBroadcastAssistantCallbackRegistered; + private TextWatcher mBroadcastNameTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(Editable s) { + if (mAlertDialog == null || mBroadcastErrorMessage == null) { + return; + } + boolean breakBroadcastNameRuleTextLengthMoreThanMax = + s.length() > BROADCAST_NAME_MAX_LENGTH; + boolean breakRule = breakBroadcastNameRuleTextLengthMoreThanMax || (s.length() == 0); + + if (breakBroadcastNameRuleTextLengthMoreThanMax) { + mBroadcastErrorMessage.setText( + mContext.getResources().getString( + R.string.media_output_broadcast_edit_hint_no_more_than_max, + BROADCAST_NAME_MAX_LENGTH)); + } + mBroadcastErrorMessage.setVisibility( + breakBroadcastNameRuleTextLengthMoreThanMax ? View.VISIBLE : View.INVISIBLE); + Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (positiveBtn != null) { + positiveBtn.setEnabled(breakRule ? false : true); + } + } + }; private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @@ -187,13 +235,6 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } }; - static final int METADATA_BROADCAST_NAME = 0; - static final int METADATA_BROADCAST_CODE = 1; - - private static final int MAX_BROADCAST_INFO_UPDATE = 3; - private static final int BROADCAST_CODE_MAX_LENGTH = 16; - private static final int BROADCAST_CODE_MIN_LENGTH = 4; - MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender, MediaOutputController mediaOutputController) { super(context, broadcastSender, mediaOutputController); @@ -392,13 +433,12 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { R.layout.media_output_broadcast_update_dialog, null); final EditText editText = layout.requireViewById(R.id.broadcast_edit_text); editText.setText(editString); - if (isBroadcastCode) { - editText.addTextChangedListener(mTextWatcher); - } + editText.addTextChangedListener( + isBroadcastCode ? mBroadcastCodeTextWatcher : mBroadcastNameTextWatcher); mBroadcastErrorMessage = layout.requireViewById(R.id.broadcast_error_message); mAlertDialog = new Builder(mContext) .setTitle(isBroadcastCode ? R.string.media_output_broadcast_code - : R.string.media_output_broadcast_name) + : R.string.media_output_broadcast_name) .setView(layout) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.media_output_broadcast_dialog_save, diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt index 1d8fe729494d..e61650fbb163 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -18,9 +18,9 @@ package com.android.systemui.mediaprojection.appselector import android.content.ComponentName import android.os.UserHandle -import com.android.systemui.flags.FeatureFlags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel @@ -32,7 +32,7 @@ class MediaProjectionAppSelectorController constructor( private val recentTaskListProvider: RecentTaskListProvider, private val view: MediaProjectionAppSelectorView, - private val flags: FeatureFlags, + private val devicePolicyResolver: ScreenCaptureDevicePolicyResolver, @HostUserHandle private val hostUserHandle: UserHandle, @MediaProjectionAppSelector private val scope: CoroutineScope, @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName, @@ -54,11 +54,13 @@ constructor( scope.cancel() } - /** - * Removes all recent tasks that are different from the profile of the host app to avoid any - * cross-profile sharing - */ - private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = this + /** Removes all recent tasks that should be blocked according to the policy */ + private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = filter { + devicePolicyResolver.isScreenCaptureAllowed( + targetAppUserHandle = UserHandle.of(it.userId), + hostAppUserHandle = hostUserHandle + ) + } private fun List<RecentTask>.filterAppSelector(): List<RecentTask> = filter { // Only take tasks that is not the app selector diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt index 6bd33e7e5c97..b11a244b2e3e 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt @@ -47,7 +47,7 @@ constructor( return false } - if (!hostAppUserHandle.isWorkProfile() && personalProfileScreenCaptureDisabled) { + if (personalProfileScreenCaptureDisabled) { // Disable screen capturing as personal apps should not capture the screen return false } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 25272ae097a1..ccfbaf1da7b6 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -215,7 +215,7 @@ constructor( debugLog { "onShowNoteTask - opened as app bubble: $info" } } is NoteTaskLaunchMode.Activity -> { - if (activityManager.isInForeground(info.packageName)) { + if (info.isKeyguardLocked && activityManager.isInForeground(info.packageName)) { // Force note task into background by calling home. val intent = createHomeIntent() context.startActivityAsUser(intent, user) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt index fae325cc3147..442000281862 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt @@ -25,12 +25,14 @@ import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity * An entry point represents where the note task has ben called from. In rare cases, it may * represent a "re-entry" (i.e., [APP_CLIPS]). */ -enum class -NoteTaskEntryPoint { +enum class NoteTaskEntryPoint { /** @see [LaunchNoteTaskActivity] */ WIDGET_PICKER_SHORTCUT, + /** @see [LaunchNoteTaskActivity] */ + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE, + /** @see [NoteTaskQuickAffordanceConfig] */ QUICK_AFFORDANCE, diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt index 48a5933a6030..a79057e5464b 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt @@ -22,6 +22,8 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON @@ -41,40 +43,45 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskOpened(info: NoteTaskInfo) { val event = - when (info.entryPoint) { - TAIL_BUTTON -> { - if (info.isKeyguardLocked) { - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED - } else { - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + } } + + WIDGET_PICKER_SHORTCUT, + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT + + QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE + APP_CLIPS, + KEYBOARD_SHORTCUT, + null -> return } - WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT - QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE - APP_CLIPS -> return - KEYBOARD_SHORTCUT -> return - null -> return - } uiEventLogger.log(event, info.uid, info.packageName) } /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskClosed(info: NoteTaskInfo) { val event = - when (info.entryPoint) { - TAIL_BUTTON -> { - if (info.isKeyguardLocked) { - NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED - } else { - NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + } } + + WIDGET_PICKER_SHORTCUT, + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE, + QUICK_AFFORDANCE, + APP_CLIPS, + KEYBOARD_SHORTCUT, + null -> return } - WIDGET_PICKER_SHORTCUT -> return - QUICK_AFFORDANCE -> return - APP_CLIPS -> return - KEYBOARD_SHORTCUT -> return - null -> return - } uiEventLogger.log(event, info.uid, info.packageName) } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt index a75834760d30..269eb870686c 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt @@ -16,6 +16,7 @@ package com.android.systemui.notetask import android.os.UserHandle +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE /** Contextual information required to launch a Note Task by [NoteTaskController]. */ data class NoteTaskInfo( @@ -27,7 +28,7 @@ data class NoteTaskInfo( ) { val launchMode: NoteTaskLaunchMode = - if (isKeyguardLocked) { + if (isKeyguardLocked || entryPoint == WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) { NoteTaskLaunchMode.Activity } else { NoteTaskLaunchMode.AppBubble diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt index 441b9f5d0181..754c3650a5ed 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt @@ -51,7 +51,8 @@ internal object NoteTaskRoleManagerExt { val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) return ShortcutInfo.Builder(context, NoteTaskController.SHORTCUT_ID) - .setIntent(LaunchNoteTaskActivity.newIntent(context = context)) + .setIntent(LaunchNoteTaskActivity.createIntent(context)) + .setActivity(LaunchNoteTaskActivity.createComponent(context)) .setShortLabel(context.getString(R.string.note_task_button_label)) .setLongLived(true) .setIcon(icon) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 0ce20cdbe63d..f02d362501d5 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -19,6 +19,7 @@ package com.android.systemui.notetask.quickaffordance import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context +import android.content.Intent import android.hardware.input.InputSettings import android.os.Build import android.os.UserHandle @@ -42,7 +43,6 @@ import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import dagger.Lazy import java.util.concurrent.Executor @@ -122,16 +122,18 @@ constructor( isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default() isEnabled -> { PickerScreenState.Disabled( - listOf( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_notes_app_instruction - ) - ), - context.getString( - R.string.keyguard_affordance_enablement_dialog_notes_app_action - ), - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE", + R.string.notes_app_quick_affordance_unavailable_explanation + ), + actionText = + context.getString( + R.string.keyguard_affordance_enablement_dialog_notes_app_action + ), + actionIntent = + Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE).apply { + setPackage(context.packageName) + }, ) } else -> PickerScreenState.UnavailableOnDevice diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 8ca13b9776bb..7ef149de2794 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.notetask.shortcut +import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle @@ -72,7 +73,13 @@ constructor( controller.startNoteTaskProxyActivityForUser(mainUser) } } else { - controller.showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT) + val entryPoint = + if (isInMultiWindowMode) { + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE + } else { + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT + } + controller.showNoteTask(entryPoint) } finish() } @@ -80,11 +87,14 @@ constructor( companion object { /** Creates a new [Intent] set to start [LaunchNoteTaskActivity]. */ - fun newIntent(context: Context): Intent { - return Intent(context, LaunchNoteTaskActivity::class.java).apply { + fun createIntent(context: Context): Intent = + Intent(context, LaunchNoteTaskActivity::class.java).apply { // Intent's action must be set in shortcuts, or an exception will be thrown. action = Intent.ACTION_CREATE_NOTE } - } + + /** Creates a new [ComponentName] for [LaunchNoteTaskActivity]. */ + fun createComponent(context: Context): ComponentName = + ComponentName(context, LaunchNoteTaskActivity::class.java) } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 18be0aa6580d..8d3b7451c90b 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -775,9 +775,13 @@ public class PeopleSpaceWidgetManager { NotificationChannel channel, int modificationType) { if (channel.isConversation()) { - updateWidgets(mAppWidgetManager.getAppWidgetIds( - new ComponentName(mContext, PeopleSpaceWidgetProvider.class) - )); + mBgExecutor.execute(() -> { + if (mUserManager.isUserUnlocked(user)) { + updateWidgets(mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class) + )); + } + }); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 897b0e73dca0..5d028307a62d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -65,14 +65,12 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.DisplayTracker; -import dagger.Lazy; - import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; - +import dagger.Lazy; public class CustomTile extends QSTileImpl<State> implements TileChangeListener { public static final String PREFIX = "custom("; diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt index a066242fd96b..d7ae575724dd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt @@ -20,6 +20,8 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory +import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository +import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor @@ -45,6 +47,11 @@ abstract class QSPipelineModule { ): CurrentTilesInteractor @Binds + abstract fun provideInstalledTilesPackageRepository( + impl: InstalledTilesComponentRepositoryImpl + ): InstalledTilesComponentRepository + + @Binds @IntoMap @ClassKey(PrototypeCoreStartable::class) abstract fun providePrototypeCoreStartable(startable: PrototypeCoreStartable): CoreStartable diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt new file mode 100644 index 000000000000..498f403e8c7a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt @@ -0,0 +1,109 @@ +/* + * 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.qs.pipeline.data.repository + +import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE +import android.annotation.WorkerThread +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.os.UserHandle +import android.service.quicksettings.TileService +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.kotlin.isComponentActuallyEnabled +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +interface InstalledTilesComponentRepository { + + fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> +} + +@SysUISingleton +class InstalledTilesComponentRepositoryImpl +@Inject +constructor( + @Application private val applicationContext: Context, + private val packageManager: PackageManager, + @Background private val backgroundDispatcher: CoroutineDispatcher, +) : InstalledTilesComponentRepository { + + override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> = + conflatedCallbackFlow { + val receiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + trySend(Unit) + } + } + applicationContext.registerReceiverAsUser( + receiver, + UserHandle.of(userId), + INTENT_FILTER, + /* broadcastPermission = */ null, + /* scheduler = */ null + ) + + awaitClose { applicationContext.unregisterReceiver(receiver) } + } + .onStart { emit(Unit) } + .map { reloadComponents(userId) } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + + @WorkerThread + private fun reloadComponents(userId: Int): Set<ComponentName> { + return packageManager + .queryIntentServicesAsUser(INTENT, FLAGS, userId) + .mapNotNull { it.serviceInfo } + .filter { it.permission == BIND_QUICK_SETTINGS_TILE } + .filter { packageManager.isComponentActuallyEnabled(it) } + .mapTo(mutableSetOf()) { it.componentName } + } + + companion object { + private val INTENT_FILTER = + IntentFilter().apply { + addAction(Intent.ACTION_PACKAGE_ADDED) + addAction(Intent.ACTION_PACKAGE_CHANGED) + addAction(Intent.ACTION_PACKAGE_REMOVED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addDataScheme("package") + } + private val INTENT = Intent(TileService.ACTION_QS_TILE) + private val FLAGS = + ResolveInfoFlags.of( + (PackageManager.GET_SERVICES or + PackageManager.MATCH_DIRECT_BOOT_AWARE or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE) + .toLong() + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt index 3b2362f2b326..a162d113a3b2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt @@ -42,6 +42,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** Repository that tracks the current tiles. */ @@ -104,6 +106,8 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, ) : TileSpecRepository { + private val mutex = Mutex() + private val retailModeTiles by lazy { resources .getString(R.string.quick_settings_tiles_retail_mode) @@ -145,37 +149,40 @@ constructor( .flowOn(backgroundDispatcher) } - override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) { - if (tile == TileSpec.Invalid) { - return - } - val tilesList = loadTiles(userId).toMutableList() - if (tile !in tilesList) { - if (position < 0 || position >= tilesList.size) { - tilesList.add(tile) - } else { - tilesList.add(position, tile) + override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) = + mutex.withLock { + if (tile == TileSpec.Invalid) { + return + } + val tilesList = loadTiles(userId).toMutableList() + if (tile !in tilesList) { + if (position < 0 || position >= tilesList.size) { + tilesList.add(tile) + } else { + tilesList.add(position, tile) + } + storeTiles(userId, tilesList) } - storeTiles(userId, tilesList) } - } - override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) { - if (tiles.all { it == TileSpec.Invalid }) { - return - } - val tilesList = loadTiles(userId).toMutableList() - if (tilesList.removeAll(tiles)) { - storeTiles(userId, tilesList.toList()) + override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) = + mutex.withLock { + if (tiles.all { it == TileSpec.Invalid }) { + return + } + val tilesList = loadTiles(userId).toMutableList() + if (tilesList.removeAll(tiles)) { + storeTiles(userId, tilesList.toList()) + } } - } - override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) { - val filtered = tiles.filter { it != TileSpec.Invalid } - if (filtered.isNotEmpty()) { - storeTiles(userId, filtered) + override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) = + mutex.withLock { + val filtered = tiles.filter { it != TileSpec.Invalid } + if (filtered.isNotEmpty()) { + storeTiles(userId, filtered) + } } - } private suspend fun loadTiles(@UserIdInt forUser: Int): List<TileSpec> { return withContext(backgroundDispatcher) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index c579f5c3061c..ff881f767b87 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -36,6 +36,7 @@ import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.TileLifecycleManager import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel import com.android.systemui.qs.pipeline.shared.TileSpec @@ -52,6 +53,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn @@ -117,11 +120,13 @@ interface CurrentTilesInteractor : ProtoDumpable { * * Platform tiles will be kept between users, with a call to [QSTile.userSwitch] * * [CustomTile]s will only be destroyed if the user changes. */ +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class CurrentTilesInteractorImpl @Inject constructor( private val tileSpecRepository: TileSpecRepository, + private val installedTilesComponentRepository: InstalledTilesComponentRepository, private val userRepository: UserRepository, private val customTileStatePersister: CustomTileStatePersister, private val tileFactory: QSFactory, @@ -141,7 +146,7 @@ constructor( override val currentTiles: StateFlow<List<TileModel>> = _currentSpecsAndTiles.asStateFlow() // This variable should only be accessed inside the collect of `startTileCollection`. - private val specsToTiles = mutableMapOf<TileSpec, QSTile>() + private val specsToTiles = mutableMapOf<TileSpec, TileOrNotInstalled>() private val currentUser = MutableStateFlow(userTracker.userId) override val userId = currentUser.asStateFlow() @@ -149,6 +154,20 @@ constructor( private val _userContext = MutableStateFlow(userTracker.userContext) override val userContext = _userContext.asStateFlow() + private val userAndTiles = + currentUser + .flatMapLatest { userId -> + tileSpecRepository.tilesSpecs(userId).map { UserAndTiles(userId, it) } + } + .distinctUntilChanged() + .pairwise(UserAndTiles(-1, emptyList())) + .flowOn(backgroundDispatcher) + + private val installedPackagesWithTiles = + currentUser.flatMapLatest { + installedTilesComponentRepository.getInstalledTilesComponents(it) + } + init { if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { startTileCollection() @@ -158,68 +177,98 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun startTileCollection() { scope.launch { - userRepository.selectedUserInfo - .flatMapLatest { user -> + launch { + userRepository.selectedUserInfo.collect { user -> currentUser.value = user.id _userContext.value = userTracker.userContext - tileSpecRepository.tilesSpecs(user.id).map { user.id to it } } - .distinctUntilChanged() - .pairwise(-1 to emptyList()) - .flowOn(backgroundDispatcher) - .collect { (old, new) -> - val newTileList = new.second - val userChanged = old.first != new.first - val newUser = new.first - - // Destroy all tiles that are not in the new set - specsToTiles - .filter { it.key !in newTileList } - .forEach { entry -> - logger.logTileDestroyed( - entry.key, - if (userChanged) { - QSPipelineLogger.TileDestroyedReason - .TILE_NOT_PRESENT_IN_NEW_USER - } else { - QSPipelineLogger.TileDestroyedReason.TILE_REMOVED - } - ) - entry.value.destroy() - } - // MutableMap will keep the insertion order - val newTileMap = mutableMapOf<TileSpec, QSTile>() - - newTileList.forEach { tileSpec -> - if (tileSpec !in newTileMap) { - val newTile = - if (tileSpec in specsToTiles) { - processExistingTile( - tileSpec, - specsToTiles.getValue(tileSpec), - userChanged, - newUser - ) - ?: createTile(tileSpec) + } + + launch(backgroundDispatcher) { + userAndTiles + .combine(installedPackagesWithTiles) { usersAndTiles, packages -> + Data( + usersAndTiles.previousValue, + usersAndTiles.newValue, + packages, + ) + } + .collectLatest { + val newTileList = it.newData.tiles + val userChanged = it.oldData.userId != it.newData.userId + val newUser = it.newData.userId + val components = it.installedComponents + + // Destroy all tiles that are not in the new set + specsToTiles + .filter { + it.key !in newTileList && it.value is TileOrNotInstalled.Tile + } + .forEach { entry -> + logger.logTileDestroyed( + entry.key, + if (userChanged) { + QSPipelineLogger.TileDestroyedReason + .TILE_NOT_PRESENT_IN_NEW_USER + } else { + QSPipelineLogger.TileDestroyedReason.TILE_REMOVED + } + ) + (entry.value as TileOrNotInstalled.Tile).tile.destroy() + } + // MutableMap will keep the insertion order + val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>() + + newTileList.forEach { tileSpec -> + if (tileSpec !in newTileMap) { + if ( + tileSpec is TileSpec.CustomTileSpec && + tileSpec.componentName !in components + ) { + newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled } else { - createTile(tileSpec) + // Create tile here will never try to create a CustomTile that + // is not installed + val newTile = + if (tileSpec in specsToTiles) { + processExistingTile( + tileSpec, + specsToTiles.getValue(tileSpec), + userChanged, + newUser + ) + ?: createTile(tileSpec) + } else { + createTile(tileSpec) + } + if (newTile != null) { + newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile) + } } - if (newTile != null) { - newTileMap[tileSpec] = newTile } } - } - val resolvedSpecs = newTileMap.keys.toList() - specsToTiles.clear() - specsToTiles.putAll(newTileMap) - _currentSpecsAndTiles.value = newTileMap.map { TileModel(it.key, it.value) } - if (resolvedSpecs != newTileList) { - // There were some tiles that couldn't be created. Change the value in the - // repository - launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } + val resolvedSpecs = newTileMap.keys.toList() + specsToTiles.clear() + specsToTiles.putAll(newTileMap) + _currentSpecsAndTiles.value = + newTileMap + .filter { it.value is TileOrNotInstalled.Tile } + .map { + TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile) + } + logger.logTilesNotInstalled( + newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys, + newUser + ) + if (resolvedSpecs != newTileList) { + // There were some tiles that couldn't be created. Change the value in + // the + // repository + launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } + } } - } + } } } @@ -301,42 +350,66 @@ constructor( private fun processExistingTile( tileSpec: TileSpec, - qsTile: QSTile, + tileOrNotInstalled: TileOrNotInstalled, userChanged: Boolean, user: Int, ): QSTile? { - return when { - !qsTile.isAvailable -> { - logger.logTileDestroyed( - tileSpec, - QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE - ) - qsTile.destroy() - null - } - // Tile is in the current list of tiles and available. - // We have a handful of different cases - qsTile !is CustomTile -> { - // The tile is not a custom tile. Make sure they are reset to the correct user - if (userChanged) { - qsTile.userSwitch(user) - logger.logTileUserChanged(tileSpec, user) + return when (tileOrNotInstalled) { + is TileOrNotInstalled.NotInstalled -> null + is TileOrNotInstalled.Tile -> { + val qsTile = tileOrNotInstalled.tile + when { + !qsTile.isAvailable -> { + logger.logTileDestroyed( + tileSpec, + QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE + ) + qsTile.destroy() + null + } + // Tile is in the current list of tiles and available. + // We have a handful of different cases + qsTile !is CustomTile -> { + // The tile is not a custom tile. Make sure they are reset to the correct + // user + if (userChanged) { + qsTile.userSwitch(user) + logger.logTileUserChanged(tileSpec, user) + } + qsTile + } + qsTile.user == user -> { + // The tile is a custom tile for the same user, just return it + qsTile + } + else -> { + // The tile is a custom tile and the user has changed. Destroy it + qsTile.destroy() + logger.logTileDestroyed( + tileSpec, + QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED + ) + null + } } - qsTile - } - qsTile.user == user -> { - // The tile is a custom tile for the same user, just return it - qsTile - } - else -> { - // The tile is a custom tile and the user has changed. Destroy it - qsTile.destroy() - logger.logTileDestroyed( - tileSpec, - QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED - ) - null } } } + + private sealed interface TileOrNotInstalled { + object NotInstalled : TileOrNotInstalled + + @JvmInline value class Tile(val tile: QSTile) : TileOrNotInstalled + } + + private data class UserAndTiles( + val userId: Int, + val tiles: List<TileSpec>, + ) + + private data class Data( + val oldData: UserAndTiles, + val newData: UserAndTiles, + val installedComponents: Set<ComponentName>, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt index ff7d2068bc4e..8318ec99e530 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt @@ -124,6 +124,18 @@ constructor( tileListLogBuffer.log(TILE_LIST_TAG, LogLevel.DEBUG, {}, { "Using retail tiles" }) } + fun logTilesNotInstalled(tiles: Collection<TileSpec>, user: Int) { + tileListLogBuffer.log( + TILE_LIST_TAG, + LogLevel.DEBUG, + { + str1 = tiles.toString() + int1 = user + }, + { "Tiles kept for not installed packages for user $int1: $str1" } + ) + } + /** Reasons for destroying an existing tile. */ enum class TileDestroyedReason(val readable: String) { TILE_REMOVED("Tile removed from current set"), diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index f080d3dfab1d..3af75cef3d4c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -34,6 +34,7 @@ import android.view.WindowInsets import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.core.view.doOnLayout import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable @@ -220,6 +221,7 @@ constructor( override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK) override fun dispatchDemoCommand(command: String, args: Bundle) = clock.dispatchDemoCommand(command, args) + override fun onDemoModeStarted() = clock.onDemoModeStarted() override fun onDemoModeFinished() = clock.onDemoModeFinished() } @@ -259,6 +261,7 @@ constructor( resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height) lastInsets?.let { updateConstraintsForInsets(header, it) } updateResources() + updateCarrierGroupPadding() } } @@ -291,6 +294,7 @@ constructor( privacyIconsController.chipVisibilityListener = chipVisibilityListener updateVisibility() updateTransition() + updateCarrierGroupPadding() header.setOnApplyWindowInsetsListener(insetListener) @@ -298,8 +302,6 @@ constructor( val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f v.pivotX = newPivot v.pivotY = v.height.toFloat() / 2 - - mShadeCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0) } clock.setOnClickListener { launchClockActivity() } @@ -359,6 +361,14 @@ constructor( .load(context, resources.getXml(R.xml.large_screen_shade_header)) } + private fun updateCarrierGroupPadding() { + clock.doOnLayout { + val maxClockWidth = + (clock.width * resources.getFloat(R.dimen.qqs_expand_clock_scale)).toInt() + mShadeCarrierGroup.setPaddingRelative(maxClockWidth, 0, 0, 0) + } + } + private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) { val cutout = insets.displayCutout.also { this.cutout = it } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 23b5241b79ef..314566eaf8d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -633,12 +633,17 @@ class HeadsUpCoordinator @Inject constructor( mFSIUpdateCandidates.removeAll(toRemoveForFSI) } - /** When an action is pressed on a notification, end HeadsUp lifetime extension. */ + /** + * When an action is pressed on a notification, make sure we don't lifetime-extend it in the + * future by informing the HeadsUpManager, and make sure we don't keep lifetime-extending it if + * we already are. + * + * @see HeadsUpManager.setUserActionMayIndirectlyRemove + * @see HeadsUpManager.canRemoveImmediately + */ private val mActionPressListener = Consumer<NotificationEntry> { entry -> - if (mNotifsExtendingLifetime.contains(entry)) { - val removeInMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key) - mExecutor.executeDelayed({ endNotifLifetimeExtensionIfExtended(entry) }, removeInMillis) - } + mHeadsUpManager.setUserActionMayIndirectlyRemove(entry) + mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) } } private val mLifetimeExtender = object : NotifLifetimeExtender { 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 096f73f80165..1cd0f081a744 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2836,7 +2836,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } private void updateDozingState() { - Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0); + if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) { + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "Dozing", 0); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "Dozing", String.valueOf(mDozing), + 0); + } Trace.beginSection("CentralSurfaces#updateDozingState"); boolean keyguardVisible = mKeyguardStateController.isVisible(); @@ -3190,6 +3194,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void onStartedWakingUp() { + // Between onStartedWakingUp() and onFinishedWakingUp(), the system is changing the + // display power mode. To avoid jank, animations should NOT run during these power + // mode transitions, which means that whenever possible, animations should + // start running during the onFinishedWakingUp() callback instead of this callback. String tag = "CentralSurfaces#onStartedWakingUp"; DejankUtils.startDetectingBlockingIpcs(tag); mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { @@ -3234,26 +3242,41 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { updateVisibleToUser(); updateIsKeyguard(); - mDozeServiceHost.stopDozing(); - // This is intentionally below the stopDozing call above, since it avoids that we're - // unnecessarily animating the wakeUp transition. Animations should only be enabled - // once we fully woke up. - updateRevealEffect(true /* wakingUp */); - updateNotificationPanelTouchState(); - mStatusBarTouchableRegionManager.updateTouchableRegion(); - - // If we are waking up during the screen off animation, we should undo making the - // expanded visible (we did that so the LightRevealScrim would be visible). - if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) { - mShadeController.makeExpandedInvisible(); + if (!mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) { + startLockscreenTransitionFromAod(); } - }); DejankUtils.stopDetectingBlockingIpcs(tag); } + /** + * Private helper for starting the LOCKSCREEN_TRANSITION_FROM_AOD animation - only necessary + * so we can start it from either onFinishedWakingUp() or onFinishedWakingUp() depending + * on a flag value. + */ + private void startLockscreenTransitionFromAod() { + // stopDozing() starts the LOCKSCREEN_TRANSITION_FROM_AOD animation. + mDozeServiceHost.stopDozing(); + // This is intentionally below the stopDozing call above, since it avoids that we're + // unnecessarily animating the wakeUp transition. Animations should only be enabled + // once we fully woke up. + updateRevealEffect(true /* wakingUp */); + updateNotificationPanelTouchState(); + mStatusBarTouchableRegionManager.updateTouchableRegion(); + + // If we are waking up during the screen off animation, we should undo making the + // expanded visible (we did that so the LightRevealScrim would be visible). + if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) { + mShadeController.makeExpandedInvisible(); + } + } + @Override public void onFinishedWakingUp() { + if (mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) { + mNotificationShadeWindowController.batchApplyWindowLayoutParams( + this::startLockscreenTransitionFromAod); + } mWakeUpCoordinator.setFullyAwake(true); mWakeUpCoordinator.setWakingUp(false, false); if (mKeyguardStateController.isOccluded() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index ed8050a031d8..92a78541d744 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -393,6 +393,31 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } + /** + * Notes that the user took an action on an entry that might indirectly cause the system or the + * app to remove the notification. + * + * @param entry the entry that might be indirectly removed by the user's action + * + * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener + * @see #canRemoveImmediately(String) + */ + public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); + if (headsUpEntry != null) { + headsUpEntry.userActionMayIndirectlyRemove = true; + } + } + + @Override + public boolean canRemoveImmediately(@NonNull String key) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); + if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) { + return true; + } + return super.canRemoveImmediately(key); + } + @NonNull @Override protected HeadsUpEntry createAlertEntry() { @@ -421,6 +446,8 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { */ protected class HeadsUpEntry extends AlertEntry { public boolean remoteInputActive; + public boolean userActionMayIndirectlyRemove; + protected boolean expanded; protected boolean wasUnpinned; diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt new file mode 100644 index 000000000000..891ee0cf66d7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import android.annotation.WorkerThread +import android.content.pm.ComponentInfo +import android.content.pm.PackageManager +import com.android.systemui.util.Assert + +@WorkerThread +fun PackageManager.isComponentActuallyEnabled(componentInfo: ComponentInfo): Boolean { + Assert.isNotMainThread() + return when (getComponentEnabledSetting(componentInfo.componentName)) { + PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> true + PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> false + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> componentInfo.isEnabled + else -> false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index b24a69292186..f37a9b5842a7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -390,8 +390,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, public void init(int windowType, Callback callback) { initDialog(mActivityManager.getLockTaskModeState()); - mAccessibility.init(); - mController.addCallback(mControllerCallbackH, mHandler); mController.getState(); @@ -478,8 +476,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); @@ -677,6 +674,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, initRingerH(); initSettingsH(lockTaskModeState); initODICaptionsH(); + mAccessibility.init(); } private boolean isWindowGravityLeft() { @@ -930,6 +928,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, showRingerDrawer(); } }); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mRingerDrawerVibrate.setOnClickListener( new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE)); @@ -992,6 +991,19 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, : 0; } + @VisibleForTesting String getSelectedRingerContainerDescription() { + return mSelectedRingerContainer == null ? null : + mSelectedRingerContainer.getContentDescription().toString(); + } + + @VisibleForTesting void toggleRingerDrawer(boolean show) { + if (show) { + showRingerDrawer(); + } else { + hideRingerDrawer(); + } + } + /** Animates in the ringer drawer. */ private void showRingerDrawer() { if (mIsRingerDrawerOpen) { @@ -1069,12 +1081,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .start(); } - // When the ringer drawer is open, tapping the currently selected ringer will set the ringer - // to the current ringer mode. Change the content description to that, instead of the 'tap - // to change ringer mode' default. - mSelectedRingerContainer.setContentDescription( - mContext.getString(getStringDescriptionResourceForRingerMode( - mState.ringerModeInternal))); + updateSelectedRingerContainerDescription(true); mIsRingerDrawerOpen = true; } @@ -1120,14 +1127,38 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .translationY(0f) .start(); - // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the - // user to change the ringer. - mSelectedRingerContainer.setContentDescription( - mContext.getString(R.string.volume_ringer_change)); + updateSelectedRingerContainerDescription(false); mIsRingerDrawerOpen = false; } + + /** + * @param open false to set the description when drawer is closed + */ + private void updateSelectedRingerContainerDescription(boolean open) { + if (mState == null || mSelectedRingerContainer == null) return; + + String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode( + mState.ringerModeInternal)); + String tapToSelect; + + if (open) { + // When the ringer drawer is open, tapping the currently selected ringer will set the + // ringer to the current ringer mode. Change the content description to that, instead of + // the 'tap to change ringer mode' default. + tapToSelect = ""; + + } else { + // When the drawer is closed, tapping the selected ringer drawer will open it, allowing + // the user to change the ringer. The user needs to know that, and also the current mode + currentMode += ", "; + tapToSelect = mContext.getString(R.string.volume_ringer_change); + } + + mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect); + } + private void initSettingsH(int lockTaskModeState) { if (mSettingsView != null) { mSettingsView.setVisibility( @@ -1703,7 +1734,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, }); } - private int getStringDescriptionResourceForRingerMode(int mode) { + @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) { switch (mode) { case RINGER_MODE_SILENT: return R.string.volume_ringer_status_silent; @@ -1785,6 +1816,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, updateVolumeRowH(row); } updateRingerH(); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mWindow.setTitle(composeWindowTitle()); } @@ -2006,14 +2038,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (row.anim == null) { row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress); row.anim.setInterpolator(new DecelerateInterpolator()); + row.anim.addListener( + getJankListener(row.view, TYPE_UPDATE, UPDATE_ANIMATION_DURATION)); } else { row.anim.cancel(); row.anim.setIntValues(progress, newProgress); } row.animTargetProgress = newProgress; row.anim.setDuration(UPDATE_ANIMATION_DURATION); - row.anim.addListener( - getJankListener(row.view, TYPE_UPDATE, UPDATE_ANIMATION_DURATION)); row.anim.start(); } else { // update slider directly to clamped value diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 254f9531ef83..061340e385a5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -72,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { private FrameLayout mSmallClockFrame; private FrameLayout mLargeClockFrame; + private KeyguardStatusAreaView mStatusArea; KeyguardClockSwitch mKeyguardClockSwitch; @@ -109,6 +110,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); + mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); mKeyguardClockSwitch.mChildrenAreLaidOut = true; } @@ -185,6 +187,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -206,6 +209,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -226,6 +230,31 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test + public void switchingToSmallClockAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, true); + + mKeyguardClockSwitch.mClockInAnim.end(); + mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + @Test + public void switchingToSmallClockNoAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, false); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + + @Test public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue(); assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt new file mode 100644 index 000000000000..e6b696454d42 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt @@ -0,0 +1,44 @@ +package com.android.keyguard + +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class KeyguardStatusAreaViewTest : SysuiTestCase() { + + private lateinit var view: KeyguardStatusAreaView + + @Before + fun setUp() { + view = KeyguardStatusAreaView(context) + } + + @Test + fun checkTranslationX_AddedTotals() { + view.translateXFromClockDesign = 10f + assertEquals(10f, view.translationX) + + view.translateXFromAod = 20f + assertEquals(30f, view.translationX) + + view.translateXFromUnfold = 30f + assertEquals(60f, view.translationX) + } + + @Test + fun checkTranslationY_AddedTotals() { + view.translateYFromClockSize = 10f + assertEquals(10f, view.translationY) + + view.translateYFromClockDesign = 20f + assertEquals(30f, view.translationY) + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 419d045885a8..de306d669476 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -2800,6 +2800,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { ); } + @Test + public void testOnSimStateChanged_Unknown() { + KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy( + KeyguardUpdateMonitorCallback.class); + mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback); + mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_UNKNOWN); + verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0, + TelephonyManager.SIM_STATE_UNKNOWN); + } + private void verifyFingerprintAuthenticateNeverCalled() { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt index 57a355f4e127..5e1a8e1432dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt @@ -37,7 +37,7 @@ class FontInterpolatorTest : SysuiTestCase() { private fun assertSameAxes(expect: Font, actual: Font) { val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } private fun assertSameAxes(expectVarSettings: String, actual: Font) { @@ -46,7 +46,7 @@ class FontInterpolatorTest : SysuiTestCase() { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } @Test @@ -61,7 +61,7 @@ class FontInterpolatorTest : SysuiTestCase() { val interp = FontInterpolator() assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f)) - assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -74,7 +74,7 @@ class FontInterpolatorTest : SysuiTestCase() { .build() val interp = FontInterpolator() - assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -118,7 +118,7 @@ class FontInterpolatorTest : SysuiTestCase() { .setFontVariationSettings("'wght' 1") .build() val resultFont = interp.lerp(startFont, endFont, 0.5f) - for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) { + for (i in 0..interp.cacheMaxEntries + 1) { val f1 = Font.Builder(sFont) .setFontVariationSettings("'wght' ${i * 100}") .build() diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt index 7fb088eb783f..bd4e8da6b326 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt @@ -20,6 +20,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -27,16 +28,20 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest class ControlsTileResourceConfigurationImplTest : SysuiTestCase() { + + @Test + fun getPackageName() { + assertThat(ControlsTileResourceConfigurationImpl().getPackageName()).isNull() + } + @Test fun getTileImageId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileImageId(), - R.drawable.controls_icon) + assertEquals(instance.getTileImageId(), R.drawable.controls_icon) } @Test fun getTileTitleId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileTitleId(), - R.string.quick_controls_title) + assertEquals(instance.getTileTitleId(), R.string.quick_controls_title) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 9144b13c7f3e..0b27bc9c7dbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -22,10 +22,10 @@ import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.systemui.SysuiTestCase -import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.ControlsTileResourceConfiguration import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController @@ -40,32 +40,25 @@ import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.`when` -import org.mockito.Mockito.any import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class ControlsComponentTest : SysuiTestCase() { - @Mock - private lateinit var controller: ControlsController - @Mock - private lateinit var uiController: ControlsUiController - @Mock - private lateinit var listingController: ControlsListingController - @Mock - private lateinit var keyguardStateController: KeyguardStateController - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private lateinit var userTracker: UserTracker - @Mock - private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var controller: ControlsController + @Mock private lateinit var uiController: ControlsUiController + @Mock private lateinit var listingController: ControlsListingController + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var userTracker: UserTracker + @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var optionalControlsTileResourceConfiguration: - Optional<ControlsTileResourceConfiguration> - @Mock - private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration + Optional<ControlsTileResourceConfiguration> + @Mock private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository @@ -123,8 +116,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(false) val component = setupComponent(true) @@ -134,8 +126,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(true) val component = setupComponent(true) @@ -146,8 +137,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() { controlsSettingsRepository.setCanShowControlsInLockscreen(false) - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(true) val component = setupComponent(true) @@ -158,8 +148,7 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileImageId() { val tileImageId = 0 - `when`(controlsTileResourceConfiguration.getTileImageId()) - .thenReturn(tileImageId) + `when`(controlsTileResourceConfiguration.getTileImageId()).thenReturn(tileImageId) val component = setupComponent(true) assertEquals(component.getTileImageId(), tileImageId) } @@ -168,12 +157,19 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileTitleId() { val tileTitleId = 0 - `when`(controlsTileResourceConfiguration.getTileTitleId()) - .thenReturn(tileTitleId) + `when`(controlsTileResourceConfiguration.getTileTitleId()).thenReturn(tileTitleId) val component = setupComponent(true) assertEquals(component.getTileTitleId(), tileTitleId) } + @Test + fun getPackageName() { + val packageName = "packageName" + `when`(controlsTileResourceConfiguration.getPackageName()).thenReturn(packageName) + val component = setupComponent(true) + assertEquals(component.getPackageName(), packageName) + } + private fun setupComponent(enabled: Boolean): ControlsComponent { return ControlsComponent( enabled, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 8ee7d3e86265..cdc99af3cbfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffor import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -98,6 +99,7 @@ class CustomizationProviderTest : SysuiTestCase() { @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var underTest: CustomizationProvider private lateinit var testScope: TestScope @@ -111,6 +113,7 @@ class CustomizationProviderTest : SysuiTestCase() { whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() underTest = CustomizationProvider() val testDispatcher = UnconfinedTestDispatcher() @@ -197,6 +200,7 @@ class CustomizationProviderTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index f8cb40885d21..1815ea9530e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.app.Activity import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase @@ -24,6 +25,7 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -75,6 +77,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes @Mock private lateinit var component: ControlsComponent @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var controlsListingController: ControlsListingController + @Mock private lateinit var controlsUiController: ControlsUiController @Mock private lateinit var controlsServiceInfo: ControlsServiceInfo @Captor private lateinit var callbackCaptor: @@ -98,6 +101,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) + whenever(controlsUiController.resolveActivity()).thenReturn(FakeActivity::class.java) + whenever(component.getControlsUiController()).thenReturn(Optional.of(controlsUiController)) if (hasPanels) { whenever(controlsServiceInfo.panelActivity).thenReturn(mock()) } @@ -178,4 +183,6 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes ) job.cancel() } + + private class FakeActivity : Activity() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt new file mode 100644 index 000000000000..7941a23fe30d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.quickaffordance + +import android.content.Intent +import android.net.Uri +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardQuickAffordanceConfigTest : SysuiTestCase() { + + @Test + fun appStoreIntent() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNotNull() + assertThat(intent?.`package`).isEqualTo("app.store.package.name") + assertThat(intent?.action).isEqualTo(Intent.ACTION_VIEW) + assertThat(intent?.data).isEqualTo(Uri.parse("prefix?id=$packageName")) + } + + @Test + fun appStoreIntent_packageNameNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test(expected = IllegalStateException::class) + fun appStoreIntent_packageNameMisconfigured_throwsIllegalStateException() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource( + R.string.config_appStoreAppLinkTemplate, + "prefix?id=\$misconfiguredPackageName" + ) + val packageName = "com.app.package.name" + + KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + } + + @Test + fun appStoreIntent_linkTemplateNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test + fun appStoreIntent_appPackageNameNull_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, null) + + assertThat(intent).isNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 26c0ea4bd11a..7510373ed582 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -53,7 +53,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(controller.intent).thenReturn(INTENT_1) - underTest = QrCodeScannerKeyguardQuickAffordanceConfig(mock(), controller) + underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller) } @Test @@ -77,22 +77,21 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = - runBlockingTest { - whenever(controller.isEnabledForLockScreenButton).thenReturn(true) - var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null - val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) - val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() - verify(controller).addCallback(callbackCaptor.capture()) - - whenever(controller.intent).thenReturn(INTENT_2) - callbackCaptor.value.onQRCodeScannerActivityChanged() - - assertVisibleState(latest) - - job.cancel() - verify(controller).removeCallback(callbackCaptor.value) - } + fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = runBlockingTest { + whenever(controller.isEnabledForLockScreenButton).thenReturn(true) + var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null + val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() + verify(controller).addCallback(callbackCaptor.capture()) + + whenever(controller.intent).thenReturn(INTENT_2) + callbackCaptor.value.onQRCodeScannerActivityChanged() + + assertVisibleState(latest) + + job.cancel() + verify(controller).removeCallback(callbackCaptor.value) + } @Test fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index fb21847cd4d1..2c90d530194d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -239,6 +240,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @JvmField @Parameter(4) var startActivity: Boolean = false private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var userTracker: UserTracker @Before @@ -250,6 +252,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { homeControls = FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() val quickAccessWallet = FakeKeyguardQuickAffordanceConfig( BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET @@ -339,6 +342,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 1d5971ce9adc..7cab680ef3e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -40,6 +40,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -97,6 +98,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig private lateinit var featureFlags: FakeFeatureFlags private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Before fun setUp() { @@ -119,6 +121,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { testScope = TestScope(testDispatcher) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() val localUserSelectionManager = KeyguardQuickAffordanceLocalUserSelectionManager( @@ -201,6 +204,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = context, ) @@ -314,6 +318,24 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordance_hiddenWhenUserIsInLockdownMode() = + testScope.runTest { + biometricSettingsRepository.setIsUserInLockdown(true) + quickAccessWallet.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + ) + ) + + val collectedValue by + collectLastValue( + underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END) + ) + + assertThat(collectedValue).isEqualTo(KeyguardQuickAffordanceModel.Hidden) + } + + @Test fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() = testScope.runTest { repository.setIsDozing(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index 8a36dbc86e81..73fe380f01c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -110,6 +111,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Before fun setUp() { @@ -126,6 +128,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { qrCodeScannerAffordanceConfig = FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() registry = FakeKeyguardQuickAffordanceRegistry( mapOf( @@ -240,6 +243,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index 7b673bc2b31e..f6075add6afe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -2420,7 +2420,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun playTurbulenceNoise_finishesAfterDuration() { - fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true) fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) val semanticActions = @@ -2452,6 +2451,29 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun playTurbulenceNoise_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() { + fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) + + val semanticActions = + MediaButton( + custom0 = + MediaAction( + icon = null, + action = {}, + contentDescription = "custom0", + background = null + ), + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.action0.callOnClick() + + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + } + + @Test fun outputSwitcher_hasCustomIntent_openOverLockscreen() { // When the device for a media player has an intent that opens over lockscreen val pendingIntent = mock(PendingIntent::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java index 205fa0f11a26..9dba9b5b3c3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java @@ -35,6 +35,8 @@ import android.media.session.MediaSessionManager; import android.os.PowerExemptionManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.View; +import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; @@ -58,6 +60,8 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.google.common.base.Strings; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -252,4 +256,106 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase { mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(0); } + + @Test + public void afterTextChanged_nameLengthMoreThanMax_showErrorMessage() { + ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_name_edit); + TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_name_summary); + broadcastName.setText(BROADCAST_NAME_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastNameEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 3); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void afterTextChanged_enterValidNameAfterLengthMoreThanMax_noErrorMessage() { + ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_name_edit); + TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_name_summary); + broadcastName.setText(BROADCAST_NAME_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastNameEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String testString = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 2); + editText.setText(testString); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + + // input the valid text + testString = Strings.repeat("b", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH - 100); + editText.setText(testString); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + } + + @Test + public void afterTextChanged_codeLengthMoreThanMax_showErrorMessage() { + ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_edit); + TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_code_summary); + broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastCodeEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_CODE_MAX_LENGTH + 1); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void afterTextChanged_codeLengthLessThanMin_showErrorMessage() { + ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_edit); + TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_code_summary); + broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastCodeEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_CODE_MIN_LENGTH - 1); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt index f1bbd8434b40..f25cd24dfcb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt @@ -5,14 +5,15 @@ import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verify @@ -27,23 +28,28 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { private val callerPackageName = "com.test.caller" private val callerComponentName = ComponentName(callerPackageName, "Caller") - private val hostUserHandle = UserHandle.of(123) - private val otherUserHandle = UserHandle.of(456) + private val personalUserHandle = UserHandle.of(123) + private val workUserHandle = UserHandle.of(456) private val view: MediaProjectionAppSelectorView = mock() - private val featureFlags: FeatureFlags = mock() + private val policyResolver: ScreenCaptureDevicePolicyResolver = mock() private val controller = MediaProjectionAppSelectorController( taskListProvider, view, - featureFlags, - hostUserHandle, + policyResolver, + personalUserHandle, scope, appSelectorComponentName, callerPackageName ) + @Before + fun setup() { + givenCaptureAllowed(isAllow = true) + } + @Test fun initNoRecentTasks_bindsEmptyList() { taskListProvider.tasks = emptyList() @@ -132,73 +138,57 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { } @Test - fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsAllTasks() { - givenEnterprisePoliciesFeatureFlag(enabled = false) - + fun initRecentTasksWithAppSelectorTasks_withEnterprisePolicies_bindsAllTasks() { val tasks = listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) taskListProvider.tasks = tasks controller.init() - // TODO (b/263950746): Cross-profile filtering is removed for now. This should be brought - // back with the future fix verify(view) .bind( listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) ) } @Test - fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() { - givenEnterprisePoliciesFeatureFlag(enabled = true) - + fun initRecentTasksWithAppSelectorTasks_withEnterprisePolicies_blocksAllTasks() { val tasks = listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) taskListProvider.tasks = tasks + givenCaptureAllowed(isAllow = false) controller.init() - // TODO(b/233348916) should filter depending on the policies - verify(view) - .bind( - listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), - ) - ) + verify(view).bind(emptyList()) } - private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) { - whenever(featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) - .thenReturn(enabled) + private fun givenCaptureAllowed(isAllow: Boolean) { + whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow) } private fun createRecentTask( taskId: Int, topActivityComponent: ComponentName? = null, - userId: Int = hostUserHandle.identifier + userId: Int = personalUserHandle.identifier ): RecentTask { return RecentTask( taskId = taskId, diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt index e8b6f9bd3478..c63efa1ba38a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt @@ -406,28 +406,6 @@ class IsAllowedScreenCaptureDevicePolicyResolverTest( isHostInWorkProfile = true, isTargetInWorkProfile = true, personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = false - ), - expectedScreenCaptureAllowed = true, - ), - IsScreenCaptureAllowedTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - isTargetInWorkProfile = true, - personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = true - ), - expectedScreenCaptureAllowed = true, - ), - IsScreenCaptureAllowedTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - isTargetInWorkProfile = true, - personalScreenCaptureDisabled = true, workScreenCaptureDisabled = true, disallowShareIntoManagedProfile = false ), @@ -626,26 +604,6 @@ class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest( Preconditions( isHostInWorkProfile = true, personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = false - ), - expectedScreenCaptureCompletelyDisabled = false, - ), - IsScreenCaptureCompletelyDisabledTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = true - ), - expectedScreenCaptureCompletelyDisabled = false, - ), - IsScreenCaptureCompletelyDisabledTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - personalScreenCaptureDisabled = true, workScreenCaptureDisabled = true, disallowShareIntoManagedProfile = false ), @@ -686,7 +644,8 @@ class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest( "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " + "work screen capture disabled = ${given.workScreenCaptureDisabled}, " + "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " + - "expected screen capture completely disabled = $expectedScreenCaptureCompletelyDisabled" + "expected screen capture completely disabled = " + + "$expectedScreenCaptureCompletelyDisabled" } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index e99f8b6aa47b..0954f6f0ffaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -72,6 +72,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.doNothing import org.mockito.Mockito.never import org.mockito.Mockito.spy @@ -227,12 +228,15 @@ internal class NoteTaskControllerTest : SysuiTestCase() { // region showNoteTask @Test - fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() { - val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true) + fun showNoteTaskAsUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() { + val user10 = UserHandle.of(/* userId= */ 10) + val expectedInfo = + NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) - createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) + createNoteTaskController() + .showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10) val intentCaptor = argumentCaptor<Intent>() val userCaptor = argumentCaptor<UserHandle>() @@ -245,21 +249,18 @@ internal class NoteTaskControllerTest : SysuiTestCase() { hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT) extras().bool(EXTRA_USE_STYLUS_MODE).isTrue() } - assertThat(userCaptor.value).isEqualTo(userTracker.userHandle) + assertThat(userCaptor.value).isEqualTo(user10) verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) } @Test - fun showNoteTaskWithUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() { - val user10 = UserHandle.of(/* userId= */ 10) - val expectedInfo = - NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10) + fun showNoteTask_keyguardIsLocked_notesIsClosed_shouldStartActivityAndLogUiEvent() { + val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) - createNoteTaskController() - .showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10) + createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) val intentCaptor = argumentCaptor<Intent>() val userCaptor = argumentCaptor<UserHandle>() @@ -272,7 +273,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT) extras().bool(EXTRA_USE_STYLUS_MODE).isTrue() } - assertThat(userCaptor.value).isEqualTo(user10) + assertThat(userCaptor.value).isEqualTo(userTracker.userHandle) verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) } @@ -301,15 +302,31 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test - fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() { + fun showNoteTask_keyguardIsUnlocked_noteIsClosed_shouldStartBubblesWithoutLoggingUiEvent() { + val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + + createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) + + // Context package name used to create bubble icon from drawable resource id + verify(context, atLeastOnce()).packageName + verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) + verifyZeroInteractions(eventLogger) + } + + @Test + fun showNoteTask_keyguardIsUnlocked_noteIsOpen_shouldStartBubblesWithoutLoggingUiEvent() { val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + whenever(activityManager.getRunningTasks(anyInt())) + .thenReturn(listOf(NOTE_RUNNING_TASK_INFO)) createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) // Context package name used to create bubble icon from drawable resource id - verify(context).packageName + verify(context, atLeastOnce()).packageName verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) verifyZeroInteractions(eventLogger) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt index 34354504abf0..24f39d187f88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt @@ -16,37 +16,47 @@ package com.android.systemui.notetask import android.os.UserHandle -import android.test.suitebuilder.annotation.SmallTest -import androidx.test.runner.AndroidJUnit4 +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith /** atest SystemUITests:NoteTaskInfoTest */ @SmallTest -@RunWith(AndroidJUnit4::class) +@RunWith(AndroidTestingRunner::class) internal class NoteTaskInfoTest : SysuiTestCase() { - private fun createNoteTaskInfo(): NoteTaskInfo = - NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0)) - @Test fun launchMode_keyguardLocked_launchModeActivity() { - val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true) + val underTest = DEFAULT_INFO.copy(isKeyguardLocked = true) assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) } @Test - fun launchMode_keyguardUnlocked_launchModeActivity() { - val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false) + fun launchMode_multiWindowMode_launchModeActivity() { + val underTest = DEFAULT_INFO.copy(entryPoint = WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) + + assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) + } + + @Test + fun launchMode_keyguardUnlocked_launchModeAppBubble() { + val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false) assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble) } private companion object { - const val NOTES_PACKAGE_NAME = "com.android.note.app" - const val NOTES_UID = 123456 + + val DEFAULT_INFO = + NoteTaskInfo( + packageName = "com.android.note.app", + uid = 123456, + user = UserHandle.of(0), + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 452658004733..58c762e3cc99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.C import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -283,7 +282,7 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest { + fun getPickerScreenState_noDefaultNoteAppSet_shouldReturnDisabled() = runTest { val underTest = createUnderTest(isEnabled = true) whenever( roleManager.getRoleHoldersAsUser( @@ -293,16 +292,16 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { ) .thenReturn(emptyList()) - assertThat(underTest.getPickerScreenState()) - .isEqualTo( - KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - listOf("Select a default notes app to use the notetaking shortcut"), - actionText = "Select app", - actionComponentName = - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" - ) - ) + val pickerScreenState = underTest.getPickerScreenState() + assertThat(pickerScreenState is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled) + .isTrue() + val disabled = pickerScreenState as KeyguardQuickAffordanceConfig.PickerScreenState.Disabled + assertThat(disabled.explanation) + .isEqualTo("Select a default notes app to use the notetaking shortcut") + assertThat(disabled.actionText).isEqualTo("Select app") + assertThat(disabled.actionIntent?.action) + .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) + assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName) } // endregion diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index e0f27de1a6de..e669d0697004 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -481,6 +481,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {1}; when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mUserManager.isUserUnlocked(any())).thenReturn(true); + NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); @@ -489,8 +491,25 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), - any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), any()); + } + + @Test + public void testOnNotificationChannelModified_userLocked() { + int[] widgetIdsArray = {1}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + when(mUserManager.isUserUnlocked(any())).thenReturn(false); + + NotificationChannel channel = + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); + channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); + + mNoMan.issueChannelModification(TEST_PACKAGE_A, + UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt new file mode 100644 index 000000000000..18f3837a7d36 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt @@ -0,0 +1,278 @@ +/* + * 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.qs.pipeline.data.repository + +import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ResolveInfo +import android.content.pm.ServiceInfo +import android.os.UserHandle +import android.service.quicksettings.TileService +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argThat +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +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.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@OptIn(ExperimentalCoroutinesApi::class) +class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Mock private lateinit var context: Context + @Mock private lateinit var packageManager: PackageManager + @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> + + private lateinit var underTest: InstalledTilesComponentRepositoryImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // Use the default value set in the ServiceInfo + whenever(packageManager.getComponentEnabledSetting(any())) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) + + // Return empty by default + whenever(packageManager.queryIntentServicesAsUser(any(), any<ResolveInfoFlags>(), anyInt())) + .thenReturn(emptyList()) + + underTest = + InstalledTilesComponentRepositoryImpl( + context, + packageManager, + testDispatcher, + ) + } + + @Test + fun registersAndUnregistersBroadcastReceiver() = + testScope.runTest { + val user = 10 + val job = launch { underTest.getInstalledTilesComponents(user).collect {} } + runCurrent() + + verify(context) + .registerReceiverAsUser( + capture(receiverCaptor), + eq(UserHandle.of(user)), + any(), + nullable(), + nullable(), + ) + + verify(context, never()).unregisterReceiver(receiverCaptor.value) + + job.cancel() + runCurrent() + verify(context).unregisterReceiver(receiverCaptor.value) + } + + @Test + fun intentFilterForCorrectActionsAndScheme() = + testScope.runTest { + val filterCaptor = argumentCaptor<IntentFilter>() + + backgroundScope.launch { underTest.getInstalledTilesComponents(0).collect {} } + runCurrent() + + verify(context) + .registerReceiverAsUser( + any(), + any(), + capture(filterCaptor), + nullable(), + nullable(), + ) + + with(filterCaptor.value) { + assertThat(matchAction(Intent.ACTION_PACKAGE_CHANGED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_ADDED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_REMOVED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_REPLACED)).isTrue() + assertThat(countActions()).isEqualTo(4) + + assertThat(hasDataScheme("package")).isTrue() + assertThat(countDataSchemes()).isEqualTo(1) + } + } + + @Test + fun componentsLoadedOnStart() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + + assertThat(componentNames).containsExactly(TEST_COMPONENT) + } + + @Test + fun componentAdded_foundAfterBroadcast() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + getRegisteredReceiver().onReceive(context, Intent(Intent.ACTION_PACKAGE_ADDED)) + + assertThat(componentNames).containsExactly(TEST_COMPONENT) + } + + @Test + fun componentWithoutPermission_notValid() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = false, defaultEnabled = true) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + } + + @Test + fun componentNotEnabled_notValid() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = false) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + } + + private fun getRegisteredReceiver(): BroadcastReceiver { + verify(context) + .registerReceiverAsUser( + capture(receiverCaptor), + any(), + any(), + nullable(), + nullable(), + ) + + return receiverCaptor.value + } + + companion object { + private val INTENT = Intent(TileService.ACTION_QS_TILE) + private val FLAGS = + ResolveInfoFlags.of( + (PackageManager.MATCH_DIRECT_BOOT_AWARE or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE or + PackageManager.GET_SERVICES) + .toLong() + ) + private val PERMISSION = BIND_QUICK_SETTINGS_TILE + + private val TEST_COMPONENT = ComponentName("pkg", "cls") + + private fun matchFlags() = + argThat<ResolveInfoFlags> { flags -> flags?.value == FLAGS.value } + private fun matchIntent() = argThat<Intent> { intent -> intent.action == INTENT.action } + + private fun ResolveInfo( + componentName: ComponentName, + hasPermission: Boolean, + defaultEnabled: Boolean + ): ResolveInfo { + val applicationInfo = ApplicationInfo().apply { enabled = true } + val serviceInfo = + ServiceInfo().apply { + packageName = componentName.packageName + name = componentName.className + if (hasPermission) { + permission = PERMISSION + } + enabled = defaultEnabled + this.applicationInfo = applicationInfo + } + val resolveInfo = ResolveInfo() + resolveInfo.serviceInfo = serviceInfo + return resolveInfo + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 426ff670802f..e7ad4896810b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.qs.external.TileLifecycleManager import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository import com.android.systemui.qs.pipeline.data.repository.FakeCustomTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel @@ -73,6 +74,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val tileSpecRepository: TileSpecRepository = FakeTileSpecRepository() private val userRepository = FakeUserRepository() + private val installedTilesPackageRepository = FakeInstalledTilesComponentRepository() private val tileFactory = FakeQSFactory(::tileCreator) private val customTileAddedRepository: CustomTileAddedRepository = FakeCustomTileAddedRepository() @@ -100,11 +102,13 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true) userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1)) + setUserTracker(0) underTest = CurrentTilesInteractorImpl( tileSpecRepository = tileSpecRepository, + installedTilesComponentRepository = installedTilesPackageRepository, userRepository = userRepository, customTileStatePersister = customTileStatePersister, tileFactory = tileFactory, @@ -609,6 +613,40 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { assertThat((tileA as FakeQSTile).callbacks).containsExactly(callback) } + @Test + fun packageNotInstalled_customTileNotVisible() = + testScope.runTest(USER_INFO_0) { + installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet()) + + val tiles by collectLastValue(underTest.currentTiles) + + val specs = listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC) + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + + assertThat(tiles!!.size).isEqualTo(1) + assertThat(tiles!![0].spec).isEqualTo(specs[0]) + } + + @Test + fun packageInstalledLater_customTileAdded() = + testScope.runTest(USER_INFO_0) { + installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet()) + + val tiles by collectLastValue(underTest.currentTiles) + val specs = listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC, TileSpec.create("b")) + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + + assertThat(tiles!!.size).isEqualTo(2) + + installedTilesPackageRepository.setInstalledPackagesForUser( + USER_INFO_0.id, + setOf(TEST_COMPONENT) + ) + + assertThat(tiles!!.size).isEqualTo(3) + assertThat(tiles!![1].spec).isEqualTo(CUSTOM_TILE_SPEC) + } + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label @@ -654,6 +692,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private suspend fun switchUser(user: UserInfo) { setUserTracker(user.id) + installedTilesPackageRepository.setInstalledPackagesForUser(user.id, setOf(TEST_COMPONENT)) userRepository.setSelectedUserInfo(user) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index 20da8a619100..2da2e9238d0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -78,6 +78,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.reset +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @@ -387,7 +388,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever(clock.isLayoutRtl).thenReturn(false) val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7) verify(clock).pivotX = 0f @@ -400,7 +401,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever(clock.isLayoutRtl).thenReturn(true) val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7) verify(clock).pivotX = width.toFloat() @@ -793,7 +794,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { @Test fun clockPivotYInCenter() { val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) var height = 100 val width = 50 @@ -825,16 +826,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test - fun carrierLeftPaddingIsSetWhenClockLayoutChanges() { - val width = 200 - whenever(clock.width).thenReturn(width) - whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml - val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) + fun carrierStartPaddingIsSetOnClockLayout() { + val clockWidth = 200 + val maxClockScale = context.resources.getFloat(R.dimen.qqs_expand_clock_scale) + val expectedStartPadding = (clockWidth * maxClockScale).toInt() + whenever(clock.width).thenReturn(clockWidth) - verify(clock).addOnLayoutChangeListener(capture(captor)) - captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0) + val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) + captor.allValues.forEach { clock.executeLayoutChange(0, 0, clockWidth, 0, it) } - verify(carrierGroup).setPaddingRelative(514, 0, 0, 0) + verify(carrierGroup).setPaddingRelative(expectedStartPadding, 0, 0, 0) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index eef4470c48cd..04c93cb71e42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -298,6 +298,54 @@ class ClockRegistryTest : SysuiTestCase() { } @Test + fun unknownPluginAttached_clockAndListUnchanged_loadRequested() { + val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package") + + var changeCallCount = 0 + var listChangeCallCount = 0 + registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener { + override fun onCurrentClockChanged() { changeCallCount++ } + override fun onAvailableClocksChanged() { listChangeCallCount++ } + }) + + assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle)) + scheduler.runCurrent() + assertEquals(0, changeCallCount) + assertEquals(0, listChangeCallCount) + } + + @Test + fun knownPluginAttached_clockAndListChanged_notLoaded() { + val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one") + val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two") + + var changeCallCount = 0 + var listChangeCallCount = 0 + registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener { + override fun onCurrentClockChanged() { changeCallCount++ } + override fun onAvailableClocksChanged() { listChangeCallCount++ } + }) + + registry.applySettings(ClockSettings("DIGITAL_CLOCK_CALLIGRAPHY", null)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(0, listChangeCallCount) + + assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(1, listChangeCallCount) + + assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(2, listChangeCallCount) + } + + @Test fun pluginAddRemove_concurrentModification() { val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index d5e904c636d5..88d853e244e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -122,6 +122,7 @@ public class PluginInstanceTest extends SysuiTestCase { mPluginInstanceFactory.create( mContext, mAppInfo, wrongVersionTestPluginComponentName, TestPlugin.class, mPluginListener); + mPluginInstance.onCreate(); } @Test @@ -135,11 +136,12 @@ public class PluginInstanceTest extends SysuiTestCase { @Test public void testOnDestroy() { + mPluginInstance.onCreate(); mPluginInstance.onDestroy(); assertEquals(1, mPluginListener.mDetachedCount); assertEquals(1, mPluginListener.mUnloadCount); assertNull(mPluginInstance.getPlugin()); - assertInstances(0, -1); // Destroyed but never created + assertInstances(0, 0); // Destroyed but never created } @Test @@ -161,6 +163,16 @@ public class PluginInstanceTest extends SysuiTestCase { assertInstances(0, 0); } + @Test + public void testOnAttach_SkipLoad() { + mPluginListener.mAttachReturn = false; + mPluginInstance.onCreate(); + assertEquals(1, mPluginListener.mAttachedCount); + assertEquals(0, mPluginListener.mLoadCount); + assertEquals(null, mPluginInstance.getPlugin()); + assertInstances(0, 0); + } + // This target class doesn't matter, it just needs to have a Requires to hit the flow where // the mock version info is called. @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION) @@ -220,15 +232,17 @@ public class PluginInstanceTest extends SysuiTestCase { } public class FakeListener implements PluginListener<TestPlugin> { + public boolean mAttachReturn = true; public int mAttachedCount = 0; public int mDetachedCount = 0; public int mLoadCount = 0; public int mUnloadCount = 0; @Override - public void onPluginAttached(PluginLifecycleManager<TestPlugin> manager) { + public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) { mAttachedCount++; assertEquals(PluginInstanceTest.this.mPluginInstance, manager); + return mAttachReturn; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 283efe263f04..257cc5b1b85c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -221,16 +221,35 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { } @Test - fun hunExtensionCancelledWhenHunActionPressed() { + fun actionPressCancelsExistingLifetimeExtension() { whenever(headsUpManager.isSticky(anyString())).thenReturn(true) addHUN(entry) + whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false) whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L) - assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0)) + assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason = */ 0)) + actionPressListener.accept(entry) - executor.advanceClockToLast() executor.runAllReady() - verify(headsUpManager, times(1)).removeNotification(eq(entry.key), eq(true)) + verify(endLifetimeExtension, times(1)).onEndLifetimeExtension(notifLifetimeExtender, entry) + + collectionListener.onEntryRemoved(entry, /* reason = */ 0) + verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any()) + } + + @Test + fun actionPressPreventsFutureLifetimeExtension() { + whenever(headsUpManager.isSticky(anyString())).thenReturn(true) + addHUN(entry) + + actionPressListener.accept(entry) + verify(headsUpManager, times(1)).setUserActionMayIndirectlyRemove(entry) + + whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true) + assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0)) + + collectionListener.onEntryRemoved(entry, /* reason = */ 0) + verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any()) } @Test 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 fd9f6a73aee4..8062272a0042 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 @@ -350,6 +350,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // For the Shade to animate during the Back gesture, we must enable the animation flag. mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true); mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true); + mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, @@ -1103,8 +1104,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { clearInvocations(mNotificationPanelViewController); mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp(); - verify(mDozeServiceHost).stopDozing(); + verify(mDozeServiceHost, never()).stopDozing(); verify(mNotificationPanelViewController).expand(eq(false)); + mCentralSurfaces.mWakefulnessObserver.onFinishedWakingUp(); + verify(mDozeServiceHost).stopDozing(); } @Test @@ -1118,6 +1121,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.setBouncerShowing(true); mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp(); verify(mNotificationPanelViewController, never()).expand(anyBoolean()); + mCentralSurfaces.mWakefulnessObserver.onFinishedWakingUp(); + verify(mDozeServiceHost).stopDozing(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index 487d26d21824..a797e032a353 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -346,4 +345,17 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } + + @Test + public void testSetUserActionMayIndirectlyRemove() { + NotificationEntry notifEntry = new NotificationEntryBuilder() + .setSbn(createNewNotification(/* id= */ 0)) + .build(); + + mHeadsUpManager.showNotification(notifEntry); + assertFalse(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey())); + + mHeadsUpManager.setUserActionMayIndirectlyRemove(notifEntry); + assertTrue(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey())); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt new file mode 100644 index 000000000000..2013bb0a547e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt @@ -0,0 +1,153 @@ +/* + * 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.util.kotlin + +import android.content.ComponentName +import android.content.pm.ComponentInfo +import android.content.pm.PackageManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(Parameterized::class) +internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) : + SysuiTestCase() { + + @Mock private lateinit var packageManager: PackageManager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testComponentActuallyEnabled() { + whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT)) + .thenReturn(testCase.componentEnabledSetting) + val componentInfo = + mock<ComponentInfo>() { + whenever(isEnabled).thenReturn(testCase.componentIsEnabled) + whenever(componentName).thenReturn(TEST_COMPONENT) + } + + assertThat(packageManager.isComponentActuallyEnabled(componentInfo)) + .isEqualTo(testCase.expected) + } + + internal data class TestCase( + @PackageManager.EnabledState val componentEnabledSetting: Int, + val componentIsEnabled: Boolean, + val expected: Boolean, + ) { + override fun toString(): String { + return "WHEN(" + + "componentIsEnabled = $componentIsEnabled, " + + "componentEnabledSetting = ${enabledStateToString()}) then " + + "EXPECTED = $expected" + } + + private fun enabledStateToString() = + when (componentEnabledSetting) { + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> "STATE_DEFAULT" + PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> "STATE_DISABLED" + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED -> { + "STATE_DISABLED_UNTIL_USED" + } + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> "STATE_DISABLED_USER" + PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> "STATE_ENABLED" + else -> "INVALID STATE" + } + } + + companion object { + @Parameters(name = "{0}") @JvmStatic fun data(): Collection<TestCase> = testData + + private val testDataComponentIsEnabled = + listOf( + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + componentIsEnabled = true, + expected = true, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + componentIsEnabled = true, + expected = true, + ), + ) + + private val testDataComponentIsDisabled = + listOf( + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + componentIsEnabled = false, + expected = true, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + componentIsEnabled = false, + expected = false, + ), + ) + + private val testData = testDataComponentIsDisabled + testDataComponentIsEnabled + + private val TEST_COMPONENT = ComponentName("pkg", "cls") + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..ef3a33248597 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -16,13 +16,19 @@ package com.android.systemui.volume; +import static android.media.AudioManager.RINGER_MODE_NORMAL; +import static android.media.AudioManager.RINGER_MODE_SILENT; +import static android.media.AudioManager.RINGER_MODE_VIBRATE; + import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -293,7 +299,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectVibrateFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -307,7 +313,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -329,7 +335,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { // Make sure we've actually changed the ringer mode. verify(mVolumeDialogController, times(1)).setRingerMode( - AudioManager.RINGER_MODE_NORMAL, false); + RINGER_MODE_NORMAL, false); } /** @@ -511,6 +517,87 @@ public class VolumeDialogImplTest extends SysuiTestCase { } } + private enum RingerDrawerState {INIT, OPEN, CLOSE} + + @Test + public void ringerModeNormal_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT); + } + + @Test + public void ringerModeSilent_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT); + } + + @Test + public void ringerModeVibrate_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT); + } + + @Test + public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); + } + + /** + * The content description should include ringer state, and the correct one. + */ + private void assertRingerContainerDescribesItsState(int ringerMode, + RingerDrawerState drawerState) { + State state = createShellState(); + state.ringerModeInternal = ringerMode; + mDialog.onStateChangedH(state); + + mDialog.show(SHOW_REASON_UNKNOWN); + + if (drawerState != RingerDrawerState.INIT) { + // in both cases we first open the drawer + mDialog.toggleRingerDrawer(true); + + if (drawerState == RingerDrawerState.CLOSE) { + mDialog.toggleRingerDrawer(false); + } + } + + String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription(); + assumeNotNull(ringerContainerDescription); + + String ringerDescription = mContext.getString( + mDialog.getStringDescriptionResourceForRingerMode(ringerMode)); + + if (drawerState == RingerDrawerState.OPEN) { + assertEquals(ringerDescription, ringerContainerDescription); + } else { + assertNotSame(ringerDescription, ringerContainerDescription); + assertTrue(ringerContainerDescription.startsWith(ringerDescription)); + } + } + @After public void teardown() { if (mDialog != null) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt new file mode 100644 index 000000000000..ff6b7d083df7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.repository + +import android.content.ComponentName +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeInstalledTilesComponentRepository : InstalledTilesComponentRepository { + + private val installedComponentsPerUser = + mutableMapOf<Int, MutableStateFlow<Set<ComponentName>>>() + + override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> { + return getFlow(userId).asStateFlow() + } + + fun setInstalledPackagesForUser(userId: Int, components: Set<ComponentName>) { + getFlow(userId).value = components + } + + private fun getFlow(userId: Int): MutableStateFlow<Set<ComponentName>> = + installedComponentsPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) } +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1a57bc1b7a0d..ec4203e3390c 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -63,6 +63,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArrayMap; import android.util.LocalLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -334,7 +335,8 @@ public final class AutofillManagerService // of time. synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.onSwitchInputMethod(); } @@ -366,11 +368,12 @@ public final class AutofillManagerService boolean isTemporary) { mAugmentedAutofillState.setServiceInfo(userId, serviceName, isTemporary); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteAugmentedAutofillService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteAugmentedAutofillService(); } @@ -380,17 +383,46 @@ public final class AutofillManagerService private void onFieldClassificationServiceNameChanged( @UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteFieldClassificationService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteFieldClassificationService(); } } } + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl getServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = getServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl peekServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = peekServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { @@ -1038,7 +1070,8 @@ public final class AutofillManagerService mUi.hideAll(null); synchronized (mLock) { final AutofillManagerServiceImpl service = - getServiceForUserLocked(UserHandle.getCallingUserId()); + getServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); service.onBackKeyPressed(); } } @@ -1537,20 +1570,27 @@ public final class AutofillManagerService public void addClient(IAutoFillManagerClient client, ComponentName componentName, int userId, IResultReceiver receiver) { int flags = 0; - synchronized (mLock) { - final int enabledFlags = getServiceForUserLocked(userId).addClientLocked(client, - componentName); - if (enabledFlags != 0) { - flags |= enabledFlags; - } - if (sDebug) { - flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; - } - if (sVerbose) { - flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + try { + synchronized (mLock) { + final int enabledFlags = + getServiceForUserWithLocalBinderIdentityLocked(userId) + .addClientLocked(client, componentName); + if (enabledFlags != 0) { + flags |= enabledFlags; + } + if (sDebug) { + flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } + if (sVerbose) { + flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + } } + } catch (Exception ex) { + // Don't do anything, send back default flags + Log.wtf(TAG, "addClient(): failed " + ex.toString()); + } finally { + send(receiver, flags); } - send(receiver, flags); } @Override @@ -1569,7 +1609,8 @@ public final class AutofillManagerService public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setAuthenticationResultLocked(data, sessionId, authenticationId, getCallingUid()); } @@ -1578,7 +1619,8 @@ public final class AutofillManagerService @Override public void setHasCallback(int sessionId, int userId, boolean hasIt) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setHasCallback(sessionId, getCallingUid(), hasIt); } } @@ -1608,7 +1650,8 @@ public final class AutofillManagerService final int taskId = mAm.getTaskIdForActivity(activityToken, false); final long result; synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); result = service.startSessionLocked(activityToken, taskId, getCallingUid(), clientCallback, autofillId, bounds, value, hasCallback, clientActivity, compatMode, mAllowInstantService, flags); @@ -1624,51 +1667,72 @@ public final class AutofillManagerService @Override public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException { + FillEventHistory fillEventHistory = null; final int userId = UserHandle.getCallingUserId(); - FillEventHistory fillEventHistory = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - fillEventHistory = service.getFillEventHistory(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + fillEventHistory = service.getFillEventHistory(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getFillEventHistory(): failed " + ex.toString()); + } finally { + send(receiver, fillEventHistory); } - send(receiver, fillEventHistory); } @Override public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException { + UserData userData = null; final int userId = UserHandle.getCallingUserId(); - UserData userData = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserData(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserData(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserData(): failed " + ex.toString()); + } finally { + send(receiver, userData); } - send(receiver, userData); } @Override public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); UserData userData = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserDataId(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserDataId(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserDataId(): failed " + ex.toString()); + } finally { + final String userDataId = userData == null ? null : userData.getId(); + send(receiver, userDataId); } - final String userDataId = userData == null ? null : userData.getId(); - send(receiver, userDataId); } @Override @@ -1676,7 +1740,8 @@ public final class AutofillManagerService final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setUserData(getCallingUid(), userData); } else if (sVerbose) { @@ -1688,124 +1753,171 @@ public final class AutofillManagerService @Override public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); boolean enabled = false; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - enabled = service.isFieldClassificationEnabled(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + enabled = service.isFieldClassificationEnabled(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back false + Log.wtf(TAG, "isFieldClassificationEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String algorithm = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + } } - } + } + } catch (Exception ex) { + // Do not raise the exception, just send back null + Log.wtf(TAG, "getDefaultFieldClassificationAlgorithm(): failed " + ex.toString()); + } finally { + send(receiver, algorithm); } - send(receiver, algorithm); + } @Override public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, @Nullable List<ComponentName> activities, @NonNull IResultReceiver receiver) throws RemoteException { + boolean ok = false; final int userId = UserHandle.getCallingUserId(); - boolean ok; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, - getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, + getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + + userId); + } } - ok = false; } + } catch (Exception ex) { + // Do not raise the exception, return the default value + Log.wtf(TAG, "setAugmentedAutofillWhitelist(): failed " + ex.toString()); + } finally { + send(receiver, + ok ? AutofillManager.RESULT_OK + : AutofillManager.RESULT_CODE_NOT_SERVICE); } - send(receiver, - ok ? AutofillManager.RESULT_OK : AutofillManager.RESULT_CODE_NOT_SERVICE); } @Override public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String[] algorithms = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithms = service + .getAvailableFieldClassificationAlgorithms(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + } } } + } catch (Exception ex) { + // Do not raise the exception, return null + Log.wtf(TAG, "getAvailableFieldClassificationAlgorithms(): failed " + + ex.toString()); + } finally { + send(receiver, algorithms); } - send(receiver, algorithms); } @Override public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver) throws RemoteException { + ComponentName componentName = null; final int userId = UserHandle.getCallingUserId(); - ComponentName componentName = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - componentName = service.getServiceComponentName(); - } else if (sVerbose) { - Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + componentName = service.getServiceComponentName(); + } else if (sVerbose) { + Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + } } + } catch (Exception ex) { + Log.wtf(TAG, "getAutofillServiceComponentName(): failed " + ex.toString()); + } finally { + send(receiver, componentName); } - send(receiver, componentName); } @Override public void restoreSession(int sessionId, @NonNull IBinder activityToken, @NonNull IBinder appCallback, @NonNull IResultReceiver receiver) throws RemoteException { + boolean restored = false; final int userId = UserHandle.getCallingUserId(); - Objects.requireNonNull(activityToken, "activityToken"); - Objects.requireNonNull(appCallback, "appCallback"); - boolean restored = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - restored = service.restoreSession(sessionId, getCallingUid(), activityToken, - appCallback); - } else if (sVerbose) { - Slog.v(TAG, "restoreSession(): no service for " + userId); + try { + Objects.requireNonNull(activityToken, "activityToken"); + Objects.requireNonNull(appCallback, "appCallback"); + + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + restored = service.restoreSession(sessionId, getCallingUid(), activityToken, + appCallback); + } else if (sVerbose) { + Slog.v(TAG, "restoreSession(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not propagate exception, send back status + Log.wtf(TAG, "restoreSession(): failed " + ex.toString()); + } finally { + send(receiver, restored); } - send(receiver, restored); } @Override public void updateSession(int sessionId, AutofillId autoFillId, Rect bounds, AutofillValue value, int action, int flags, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds, value, action, flags); @@ -1818,7 +1930,8 @@ public final class AutofillManagerService @Override public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setAutofillFailureLocked(sessionId, getCallingUid(), ids); } else if (sVerbose) { @@ -1831,7 +1944,8 @@ public final class AutofillManagerService public void finishSession(int sessionId, int userId, @AutofillCommitReason int commitReason) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.finishSessionLocked(sessionId, getCallingUid(), commitReason); } else if (sVerbose) { @@ -1843,19 +1957,22 @@ public final class AutofillManagerService @Override public void cancelSession(int sessionId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.cancelSessionLocked(sessionId, getCallingUid()); } else if (sVerbose) { Slog.v(TAG, "cancelSession(): no service for " + userId); } } + } @Override public void disableOwnedAutofillServices(int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.disableOwnedAutofillServicesLocked(Binder.getCallingUid()); } else if (sVerbose) { @@ -1867,21 +1984,36 @@ public final class AutofillManagerService @Override public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) { boolean supported = false; - synchronized (mLock) { - supported = !isDisabledLocked(userId); + + try { + synchronized (mLock) { + supported = !isDisabledLocked(userId); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceSupported(): failed " + ex.toString()); + } finally { + send(receiver, supported); } - send(receiver, supported); } @Override public void isServiceEnabled(int userId, @NonNull String packageName, @NonNull IResultReceiver receiver) { boolean enabled = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); - enabled = Objects.equals(packageName, service.getServicePackageName()); + + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + enabled = Objects.equals(packageName, service.getServicePackageName()); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override @@ -1891,8 +2023,9 @@ public final class AutofillManagerService || operation == AutofillManager.PENDING_UI_OPERATION_RESTORE, "invalid operation: %d", operation); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked( - UserHandle.getCallingUserId()); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); if (service != null) { service.onPendingSaveUi(operation, token); } @@ -1907,7 +2040,7 @@ public final class AutofillManagerService boolean uiOnly = false; if (args != null) { for (String arg : args) { - switch(arg) { + switch (arg) { case "--no-history": showHistory = false; break; @@ -1934,27 +2067,38 @@ public final class AutofillManagerService try { sDebug = sVerbose = true; synchronized (mLock) { - pw.print("sDebug: "); pw.print(realDebug); - pw.print(" sVerbose: "); pw.println(realVerbose); + pw.print("sDebug: "); + pw.print(realDebug); + pw.print(" sVerbose: "); + pw.println(realVerbose); pw.print("Flags: "); synchronized (mFlagLock) { - pw.print("mPccClassificationEnabled="); pw.print(mPccClassificationEnabled); + pw.print("mPccClassificationEnabled="); + pw.print(mPccClassificationEnabled); pw.print(";"); - pw.print("mPccPreferProviderOverPcc="); pw.print(mPccPreferProviderOverPcc); + pw.print("mPccPreferProviderOverPcc="); + pw.print(mPccPreferProviderOverPcc); pw.print(";"); - pw.print("mPccUseFallbackDetection="); pw.print(mPccUseFallbackDetection); + pw.print("mPccUseFallbackDetection="); + pw.print(mPccUseFallbackDetection); pw.print(";"); - pw.print("mPccProviderHints="); pw.println(mPccProviderHints); + pw.print("mPccProviderHints="); + pw.println(mPccProviderHints); } // Dump per-user services dumpLocked("", pw); - mAugmentedAutofillResolver.dumpShort(pw); pw.println(); - pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount); - pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount); + mAugmentedAutofillResolver.dumpShort(pw); + pw.println(); + pw.print("Max partitions per session: "); + pw.println(sPartitionMaxCount); + pw.print("Max visible datasets: "); + pw.println(sVisibleDatasetsMaxCount); if (sFullScreenMode != null) { - pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode); + pw.print("Overridden full-screen mode: "); + pw.println(sFullScreenMode); } - pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw); + pw.println("User data constraints: "); + UserData.dumpConstraints(prefix, pw); mUi.dump(pw); pw.print("Autofill Compat State: "); mAutofillCompatState.dump(prefix, pw); @@ -1969,11 +2113,17 @@ public final class AutofillManagerService pw.print("Augmented Service Request Timeout: "); pw.println(mAugmentedServiceRequestTimeoutMs); if (showHistory) { - pw.println(); pw.println("Requests history:"); pw.println(); + pw.println(); + pw.println("Requests history:"); + pw.println(); mRequestsHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("UI latency history:"); pw.println(); + pw.println(); + pw.println("UI latency history:"); + pw.println(); mUiLatencyHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("WTF history:"); pw.println(); + pw.println(); + pw.println("WTF history:"); + pw.println(); mWtfHistory.reverseDump(fd, pw, args); } pw.println("Augmented Autofill State: "); diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 6b55d7ed4d56..cd2f844294e3 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -987,8 +987,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub "Virtual device doesn't have a virtual display with ID " + displayId); } - releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); - + final long ident = Binder.clearCallingIdentity(); + try { + releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); + } finally { + Binder.restoreCallingIdentity(ident); + } } /** diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index e8c85ce68f22..3efb6c304bec 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -1065,13 +1065,20 @@ public class VcnManagementService extends IVcnManagementService.Stub { boolean isRestricted = false; synchronized (mLock) { final Vcn vcn = mVcns.get(subGrp); + final VcnConfig vcnConfig = mConfigs.get(subGrp); if (vcn != null) { + if (vcnConfig == null) { + // TODO: b/284381334 Investigate for the root cause of this issue + // and handle it properly + logWtf("Vcn instance exists but VcnConfig does not for " + subGrp); + } + if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) { isVcnManagedNetwork = true; } final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports( - subGrp, mLastSnapshot, mConfigs.get(subGrp)); + subGrp, mLastSnapshot, vcnConfig); for (int restrictedTransport : restrictedTransports) { if (ncCopy.hasTransport(restrictedTransport)) { if (restrictedTransport == TRANSPORT_CELLULAR diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java index 2812233815a6..99178920cc52 100644 --- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java +++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java @@ -88,7 +88,7 @@ public class WallpaperUpdateReceiver extends BroadcastReceiver { } else { //live wallpaper ComponentName currCN = info.getComponent(); - ComponentName defaultCN = WallpaperManager.getCmfDefaultWallpaperComponent(context); + ComponentName defaultCN = WallpaperManager.getDefaultWallpaperComponent(context); if (!currCN.equals(defaultCN)) { return true; } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7ede4614232f..fc84e1386d1f 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -621,7 +621,8 @@ public final class ActiveServices { try { final ServiceRecord.StartItem si = r.pendingStarts.get(0); startServiceInnerLocked(this, si.intent, r, false, true, si.callingId, - si.mCallingProcessName, r.startRequested, si.mCallingPackageName); + si.mCallingProcessName, si.mCallingProcessState, + r.startRequested, si.mCallingPackageName); } catch (TransactionTooLargeException e) { // Ignore, nobody upstack cares. } @@ -977,10 +978,22 @@ public final class ActiveServices { fgRequired = false; } + final ProcessRecord callingApp; + synchronized (mAm.mPidsSelfLocked) { + callingApp = mAm.mPidsSelfLocked.get(callingPid); + } + final String callingProcessName = callingApp != null + ? callingApp.processName : callingPackage; + final int callingProcessState = + callingApp != null && callingApp.getThread() != null && !callingApp.isKilled() + ? callingApp.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + r.updateProcessStateOnRequest(); + // The package could be frozen (meaning it's doing surgery), defer the actual // start until the package is unfrozen. if (deferServiceBringupIfFrozenLocked(r, service, callingPackage, callingFeatureId, - callingUid, callingPid, fgRequired, callerFg, userId, + callingUid, callingPid, callingProcessName, + callingProcessState, fgRequired, callerFg, userId, backgroundStartPrivileges, false, null)) { return null; } @@ -1001,7 +1014,7 @@ public final class ActiveServices { // what realResult contains. final ComponentName realResult = startServiceInnerLocked(r, service, callingUid, callingPid, - getCallingProcessNameLocked(callingUid, callingPid, callingPackage), + callingProcessName, callingProcessState, fgRequired, callerFg, backgroundStartPrivileges, callingPackage); if (res.aliasComponent != null @@ -1013,17 +1026,9 @@ public final class ActiveServices { } } - private String getCallingProcessNameLocked(int callingUid, int callingPid, - String callingPackage) { - synchronized (mAm.mPidsSelfLocked) { - final ProcessRecord callingApp = mAm.mPidsSelfLocked.get(callingPid); - return callingApp != null ? callingApp.processName : callingPackage; - } - } - private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service, - int callingUid, int callingPid, String callingProcessName, boolean fgRequired, - boolean callerFg, + int callingUid, int callingPid, String callingProcessName, + int callingProcessState, boolean fgRequired, boolean callerFg, BackgroundStartPrivileges backgroundStartPrivileges, String callingPackage) throws TransactionTooLargeException { NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent( @@ -1037,7 +1042,8 @@ public final class ActiveServices { r.delayedStop = false; r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - service, neededGrants, callingUid, callingProcessName, callingPackage)); + service, neededGrants, callingUid, callingProcessName, callingPackage, + callingProcessState)); // We want to allow scheduling user-initiated jobs when the app is running a // foreground service that was started in the same conditions that allows for scheduling @@ -1140,7 +1146,8 @@ public final class ActiveServices { r.allowBgActivityStartsOnServiceStart(backgroundStartPrivileges); } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting, - callingUid, callingProcessName, wasStartRequested, callingPackage); + callingUid, callingProcessName, callingProcessState, + wasStartRequested, callingPackage); return cmp; } @@ -1244,7 +1251,8 @@ public final class ActiveServices { @GuardedBy("mAm") private boolean deferServiceBringupIfFrozenLocked(ServiceRecord s, Intent serviceIntent, String callingPackage, @Nullable String callingFeatureId, - int callingUid, int callingPid, boolean fgRequired, boolean callerFg, int userId, + int callingUid, int callingPid, String callingProcessName, + int callingProcessState, boolean fgRequired, boolean callerFg, int userId, BackgroundStartPrivileges backgroundStartPrivileges, boolean isBinding, IServiceConnection connection) { final PackageManagerInternal pm = mAm.getPackageManagerInternal(); @@ -1258,8 +1266,6 @@ public final class ActiveServices { curPendingBringups = new ArrayList<>(); mPendingBringups.put(s, curPendingBringups); } - final String callingProcessName = getCallingProcessNameLocked( - callingUid, callingPid, callingPackage); curPendingBringups.add(new Runnable() { @Override public void run() { @@ -1291,7 +1297,7 @@ public final class ActiveServices { } else { // Starting a service try { startServiceInnerLocked(s, serviceIntent, callingUid, callingPid, - callingProcessName, fgRequired, callerFg, + callingProcessName, callingProcessState, fgRequired, callerFg, backgroundStartPrivileges, callingPackage); } catch (TransactionTooLargeException e) { /* ignore - local call */ @@ -1338,7 +1344,8 @@ public final class ActiveServices { ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting, int callingUid, String callingProcessName, - boolean wasStartRequested, String callingPackage) throws TransactionTooLargeException { + int callingProcessState, boolean wasStartRequested, String callingPackage) + throws TransactionTooLargeException { synchronized (mAm.mProcessStats.mLock) { final ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1381,7 +1388,9 @@ public final class ActiveServices { getShortServiceNameForStats(r), packageState, packageName, - callingPackage); + callingPackage, + callingProcessState, + r.mProcessStateOnRequest); if (r.startRequested && addToStarting) { boolean first = smap.mStartingBackground.size() == 0; @@ -2156,9 +2165,7 @@ public final class ActiveServices { } } - final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs; - - if (fgsTypeChangingFromShortFgs) { + if (r.isForeground && isOldTypeShortFgs) { // If we get here, that means startForeground(SHORT_SERVICE) is called again // on a SHORT_SERVICE FGS. @@ -2211,9 +2218,7 @@ public final class ActiveServices { // "if (r.mAllowStartForeground == REASON_DENIED...)" block below. } } - } - - if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount == 0) { + } else if (r.mStartForegroundCount == 0) { /* If the service was started with startService(), not startForegroundService(), and if startForeground() isn't called within @@ -2244,7 +2249,7 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = false; } } - } else if (!fgsTypeChangingFromShortFgs && r.mStartForegroundCount >= 1) { + } else if (r.mStartForegroundCount >= 1) { // We get here if startForeground() is called multiple times // on the same service after it's created, regardless of whether // stopForeground() has been called or not. @@ -3615,11 +3620,22 @@ public final class ActiveServices { return 0; } + final ProcessRecord callingApp; + synchronized (mAm.mPidsSelfLocked) { + callingApp = mAm.mPidsSelfLocked.get(callingPid); + } + final String callingProcessName = callingApp != null + ? callingApp.processName : callingPackage; + final int callingProcessState = + callingApp != null && callingApp.getThread() != null && !callingApp.isKilled() + ? callingApp.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + s.updateProcessStateOnRequest(); + // The package could be frozen (meaning it's doing surgery), defer the actual // binding until the package is unfrozen. boolean packageFrozen = deferServiceBringupIfFrozenLocked(s, service, callingPackage, null, - callingUid, callingPid, false, callerFg, userId, BackgroundStartPrivileges.NONE, - true, connection); + callingUid, callingPid, callingProcessName, callingProcessState, + false, callerFg, userId, BackgroundStartPrivileges.NONE, true, connection); // If permissions need a review before any of the app components can run, // we schedule binding to the service but do not start its process, then @@ -3760,7 +3776,9 @@ public final class ActiveServices { getShortServiceNameForStats(s), packageState, s.packageName, - callerApp.info.packageName); + callerApp.info.packageName, + callerApp.mState.getCurProcState(), + s.mProcessStateOnRequest); if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b + ": received=" + b.intent.received @@ -5359,7 +5377,7 @@ public final class ActiveServices { // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - null, null, 0, null, null)); + null, null, 0, null, null, ActivityManager.PROCESS_STATE_UNKNOWN)); } sendServiceArgsLocked(r, execInFg, true); @@ -6355,7 +6373,8 @@ public final class ActiveServices { stopServiceLocked(sr, true); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, - sr.getLastStartId(), baseIntent, null, 0, null, null)); + sr.getLastStartId(), baseIntent, null, 0, null, null, + ActivityManager.PROCESS_STATE_UNKNOWN)); if (sr.app != null && sr.app.getThread() != null) { // We always run in the foreground, since this is called as // part of the "remove task" UI operation. @@ -7436,14 +7455,10 @@ public final class ActiveServices { r.mAllowWhileInUsePermissionInFgs = true; } - // Either (or both) mAllowWhileInUsePermissionInFgs or mAllowStartForeground is - // newly allowed? - boolean newlyAllowed = false; if (!r.mAllowWhileInUsePermissionInFgs || (r.mAllowStartForeground == REASON_DENIED)) { @ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( - callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges, - isBindService); + callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges); // We store them to compare the old and new while-in-use logics to each other. // (They're not used for any other purposes.) if (!r.mAllowWhileInUsePermissionInFgs) { @@ -7477,7 +7492,7 @@ public final class ActiveServices { } final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( callingPackage, callingPid, callingUid, null /* targetProcess */, - BackgroundStartPrivileges.NONE, false); + BackgroundStartPrivileges.NONE); @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked( allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */, BackgroundStartPrivileges.NONE); @@ -7501,19 +7516,15 @@ public final class ActiveServices { */ private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, int callingPid, int callingUid, @Nullable ProcessRecord targetProcess, - BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) { + BackgroundStartPrivileges backgroundStartPrivileges) { int ret = REASON_DENIED; - final boolean forStartForeground = !isBindService; - - if (forStartForeground) { - final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == REASON_DENIED) { - // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT, - // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP. - if (uidState <= PROCESS_STATE_TOP) { - ret = getReasonCodeFromProcState(uidState); - } + final int uidState = mAm.getUidStateLocked(callingUid); + if (ret == REASON_DENIED) { + // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT, + // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP. + if (uidState <= PROCESS_STATE_TOP) { + ret = getReasonCodeFromProcState(uidState); } } @@ -7734,7 +7745,7 @@ public final class ActiveServices { shouldAllowFgsWhileInUsePermissionLocked( clientPackageName, clientPid, clientUid, null /* targetProcess */, - BackgroundStartPrivileges.NONE, false); + BackgroundStartPrivileges.NONE); final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked( allowWhileInUse2, @@ -8163,7 +8174,7 @@ public final class ActiveServices { String callingPackage) { return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid, /* targetProcess */ null, - BackgroundStartPrivileges.NONE, false) + BackgroundStartPrivileges.NONE) != REASON_DENIED; } @@ -8171,7 +8182,7 @@ public final class ActiveServices { String callingPackage, @Nullable ProcessRecord targetProcess, @NonNull BackgroundStartPrivileges backgroundStartPrivileges) { return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid, - targetProcess, backgroundStartPrivileges, false) != REASON_DENIED; + targetProcess, backgroundStartPrivileges) != REASON_DENIED; } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 44e198b53761..8c31209aeeb4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -1678,9 +1678,9 @@ final class ActivityManagerConstants extends ContentObserver { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME, DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS); - if (mKillBgRestrictedAndCachedIdleSettleTimeMs != currentSettleTime) { - mService.mHandler.removeMessages( - ActivityManagerService.IDLE_UIDS_MSG); + if (mKillBgRestrictedAndCachedIdleSettleTimeMs < currentSettleTime) { + // Don't remove existing messages in case other IDLE_UIDS_MSG initiators use lower + // delays, but send a new message if the settle time has decreased. mService.mHandler.sendEmptyMessageDelayed( ActivityManagerService.IDLE_UIDS_MSG, mKillBgRestrictedAndCachedIdleSettleTimeMs); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5c1dad978095..0916960fae13 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1176,20 +1176,24 @@ public class ActivityManagerService extends IActivityManager.Stub public Intent intent; public boolean deferUntilActive; public int originalCallingUid; + /** The snapshot process state of the app who sent this broadcast */ + public int originalCallingAppProcessState; public static StickyBroadcast create(Intent intent, boolean deferUntilActive, - int originalCallingUid) { + int originalCallingUid, int originalCallingAppProcessState) { final StickyBroadcast b = new StickyBroadcast(); b.intent = intent; b.deferUntilActive = deferUntilActive; b.originalCallingUid = originalCallingUid; + b.originalCallingAppProcessState = originalCallingAppProcessState; return b; } @Override public String toString() { return "{intent=" + intent + ", defer=" + deferUntilActive + ", originalCallingUid=" - + originalCallingUid + "}"; + + originalCallingUid + ", originalCallingAppProcessState=" + + originalCallingAppProcessState + "}"; } } @@ -1522,6 +1526,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ int mBootPhase; + volatile boolean mDeterministicUidIdle = false; + @VisibleForTesting public WindowManagerService mWindowManager; WindowManagerInternal mWmInternal; @@ -1616,6 +1622,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final int SERVICE_SHORT_FGS_PROCSTATE_TIMEOUT_MSG = 77; static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 78; static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79; + static final int ADD_UID_TO_OBSERVER_MSG = 80; + static final int REMOVE_UID_FROM_OBSERVER_MSG = 81; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1774,6 +1782,12 @@ public class ActivityManagerService extends IActivityManager.Stub case PUSH_TEMP_ALLOWLIST_UI_MSG: { pushTempAllowlist(); } break; + case ADD_UID_TO_OBSERVER_MSG: { + mUidObserverController.addUidToObserverImpl((IBinder) msg.obj, msg.arg1); + } break; + case REMOVE_UID_FROM_OBSERVER_MSG: { + mUidObserverController.removeUidFromObserverImpl((IBinder) msg.obj, msg.arg1); + } break; } } } @@ -13785,7 +13799,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (action.startsWith("android.intent.action.USER_") || action.startsWith("android.intent.action.PACKAGE_") || action.startsWith("android.intent.action.UID_") - || action.startsWith("android.intent.action.EXTERNAL_")) { + || action.startsWith("android.intent.action.EXTERNAL_") + || action.startsWith("android.bluetooth.")) { if (DEBUG_BROADCAST) { Slog.wtf(TAG, "System internals registering for " + filter.toLongString() @@ -14033,7 +14048,8 @@ public class ActivityManagerService extends IActivityManager.Stub receivers, null, null, 0, null, null, false, true, true, -1, originalStickyCallingUid, BackgroundStartPrivileges.NONE, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */, - null /* filterExtrasForReceiver */); + null /* filterExtrasForReceiver */, + broadcast.originalCallingAppProcessState); queue.enqueueBroadcastLocked(r); } } @@ -14848,6 +14864,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final int callerAppProcessState = getRealProcessStateLocked(callerApp, realCallingPid); // Add to the sticky list if requested. if (sticky) { if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, @@ -14910,12 +14927,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (intent.filterEquals(list.get(i).intent)) { // This sticky already exists, replace it. list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive, - callingUid)); + callingUid, callerAppProcessState)); break; } } if (i >= stickiesCount) { - list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid)); + list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid, + callerAppProcessState)); } } @@ -14998,7 +15016,8 @@ public class ActivityManagerService extends IActivityManager.Stub requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, registeredReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerAppProcessState); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); queue.enqueueBroadcastLocked(r); registeredReceivers = null; @@ -15092,7 +15111,8 @@ public class ActivityManagerService extends IActivityManager.Stub requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerAppProcessState); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); queue.enqueueBroadcastLocked(r); @@ -15109,6 +15129,19 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManager.BROADCAST_SUCCESS; } + @GuardedBy("this") + private int getRealProcessStateLocked(ProcessRecord app, int pid) { + if (app == null) { + synchronized (mPidsSelfLocked) { + app = mPidsSelfLocked.get(pid); + } + } + if (app != null && app.getThread() != null && !app.isKilled()) { + return app.mState.getCurProcState(); + } + return PROCESS_STATE_NONEXISTENT; + } + @VisibleForTesting ArrayList<StickyBroadcast> getStickyBroadcasts(String action, int userId) { final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts = @@ -16463,6 +16496,11 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void setDeterministicUidIdle(boolean deterministic) { + mDeterministicUidIdle = deterministic; + } + /** Make the currently active UIDs idle after a certain grace period. */ final void idleUids() { synchronized (this) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index add22bd6009e..fd980727e12b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -290,6 +290,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runKillAll(pw); case "make-uid-idle": return runMakeIdle(pw); + case "set-deterministic-uid-idle": + return runSetDeterministicUidIdle(pw); case "monitor": return runMonitor(pw); case "watch-uids": @@ -1520,6 +1522,23 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runSetDeterministicUidIdle(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + boolean deterministic = Boolean.parseBoolean(getNextArgRequired()); + mInterface.setDeterministicUidIdle(deterministic); + return 0; + } + static final class MyActivityController extends IActivityController.Stub { final IActivityManager mInterface; final PrintWriter mPw; @@ -4271,6 +4290,11 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>"); pw.println(" If the given application's uid is in the background and waiting to"); pw.println(" become idle (not allowing background services), do that now."); + pw.println( + " set-deterministic-uid-idle [--user <USER_ID> | all | current] <true|false>"); + pw.println(" If true, sets the timing of making UIDs idle consistent and"); + pw.println(" deterministic. If false, the timing will be variable depending on"); + pw.println(" other activity on the device. The default is false."); pw.println(" monitor [--gdb <port>] [-p <TARGET>] [-s] [-c] [-k]"); pw.println(" Start monitoring for crashes or ANRs."); pw.println(" --gdb: start gdbserv on the given port at crash/ANR"); diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 0b5b1cb2902e..a80ad599a3e2 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -25,6 +25,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; +import android.app.BroadcastOptions; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.SystemClock; @@ -105,6 +106,12 @@ class BroadcastProcessQueue { long lastCpuDelayTime; /** + * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before + * dispatching the current broadcast to the receiver in this process. + */ + int lastProcessState; + + /** * Ordered collection of broadcasts that are waiting to be dispatched to * this process, as a pair of {@link BroadcastRecord} and the index into * {@link BroadcastRecord#receivers} that represents the receiver. @@ -257,7 +264,10 @@ class BroadcastProcessQueue { deferredStatesApplyConsumer.accept(record, recordIndex); } - if (record.isReplacePending()) { + // Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the + // BroadcastOptions delivery group APIs. + if (record.isReplacePending() + && record.getDeliveryGroupPolicy() == BroadcastOptions.DELIVERY_GROUP_POLICY_ALL) { final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex); if (replacedBroadcastRecord != null) { return replacedBroadcastRecord; diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index f6004d7d2b7f..f13dc89f2bd2 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -269,7 +269,10 @@ public class BroadcastQueueImpl extends BroadcastQueue { Activity.RESULT_CANCELED, null, null, false, false, oldRecord.shareIdentity, oldRecord.userId, oldRecord.callingUid, r.callingUid, r.callerPackage, - SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0); + SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0, 0, + oldRecord.resultToApp != null + ? oldRecord.resultToApp.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); } catch (RemoteException e) { Slog.w(TAG, "Failure [" + mQueueName + "] sending broadcast result of " @@ -339,7 +342,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue, BroadcastRecord r, String typeForLogging) { final Intent intent = r.intent; - for (int i = queue.size() - 1; i > 0; i--) { + for (int i = queue.size() - 1; i >= 0; i--) { final BroadcastRecord old = queue.get(i); if (old.userId == r.userId && intent.filterEquals(old.intent)) { if (DEBUG_BROADCAST) { @@ -367,6 +370,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { } r.curApp = app; + r.curAppLastProcessState = app.mState.getCurProcState(); final ProcessReceiverRecord prr = app.mReceivers; prr.addCurReceiver(r); app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); @@ -418,6 +422,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Process cur broadcast " + r + ": NOT STARTED!"); r.curApp = null; + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; prr.removeCurReceiver(r); } } @@ -617,7 +622,11 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.curApp.info.packageName, r.callerPackage, r.calculateTypeForLogging(), - r.getDeliveryGroupPolicy()); + r.getDeliveryGroupPolicy(), + r.intent.getFlags(), + BroadcastRecord.getReceiverPriority(curReceiver), + r.callerProcState, + r.curAppLastProcessState); } if (state == BroadcastRecord.IDLE) { Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE"); @@ -679,6 +688,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.curFilter = null; r.curReceiver = null; r.curApp = null; + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; r.curFilteredExtras = null; r.mWasReceiverAppStopped = false; mPendingBroadcast = null; @@ -748,7 +758,8 @@ public class BroadcastQueueImpl extends BroadcastQueue { Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, boolean shareIdentity, int sendingUser, int receiverUid, int callingUid, String callingPackage, - long dispatchDelay, long receiveDelay) throws RemoteException { + long dispatchDelay, long receiveDelay, int priority, + int receiverProcessState) throws RemoteException { // If the broadcaster opted-in to sharing their identity, then expose package visibility for // the receiver. if (shareIdentity) { @@ -798,7 +809,8 @@ public class BroadcastQueueImpl extends BroadcastQueue { dispatchDelay, receiveDelay, 0 /* finish_delay */, SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, app != null ? app.info.packageName : null, callingPackage, - r.calculateTypeForLogging(), r.getDeliveryGroupPolicy()); + r.calculateTypeForLogging(), r.getDeliveryGroupPolicy(), r.intent.getFlags(), + priority, r.callerProcState, receiverProcessState); } } @@ -845,6 +857,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { // things that directly call the IActivityManager API, which // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; + r.curAppLastProcessState = r.curApp.mState.getCurProcState(); filter.receiverList.app.mReceivers.addCurReceiver(r); mService.enqueueOomAdjTargetLocked(r.curApp); mService.updateOomAdjPendingTargetsLocked( @@ -879,7 +892,10 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.resultExtras, r.ordered, r.initialSticky, r.shareIdentity, r.userId, filter.receiverList.uid, r.callingUid, r.callerPackage, r.dispatchTime - r.enqueueTime, - r.receiverTime - r.dispatchTime); + r.receiverTime - r.dispatchTime, filter.getPriority(), + filter.receiverList.app != null + ? filter.receiverList.app.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); // parallel broadcasts are fire-and-forget, not bookended by a call to // finishReceiverLocked(), so we manage their activity-start token here if (filter.receiverList.app != null @@ -1170,7 +1186,10 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.resultData, r.resultExtras, false, false, r.shareIdentity, r.userId, r.callingUid, r.callingUid, r.callerPackage, r.dispatchTime - r.enqueueTime, - now - r.dispatchTime); + now - r.dispatchTime, 0, + r.resultToApp != null + ? r.resultToApp.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); logBootCompletedBroadcastCompletionLatencyIfPossible(r); // Set this to null so that the reference // (local and remote) isn't kept in the mBroadcastHistory. @@ -1476,6 +1495,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.intent.getAction(), r.getHostingRecordTriggerType()), isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false); + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; if (r.curApp == null) { // Ah, this recipient is unavailable. Finish it if necessary, // and mark the broadcast record as ready for the next. diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 55f8cef019f8..76af50de0bd7 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -728,11 +728,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private void skipAndCancelReplacedBroadcasts(ArraySet<BroadcastRecord> replacedBroadcasts) { for (int i = 0; i < replacedBroadcasts.size(); ++i) { final BroadcastRecord r = replacedBroadcasts.valueAt(i); - r.resultCode = Activity.RESULT_CANCELED; - r.resultData = null; - r.resultExtras = null; - scheduleResultTo(r); - notifyFinishBroadcast(r); + // Skip all the receivers in the replaced broadcast + for (int rcvrIdx = 0; rcvrIdx < r.receivers.size(); ++rcvrIdx) { + if (!isDeliveryStateTerminal(r.getDeliveryState(rcvrIdx))) { + mBroadcastConsumerSkipAndCanceled.accept(r, rcvrIdx); + } + } } } @@ -1036,6 +1037,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent, UserHandle.getAppId(app.uid), r.callingUid, true); } + queue.lastProcessState = app.mState.getCurProcState(); if (receiver instanceof BroadcastFilter) { notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver); thread.scheduleRegisteredReceiver( @@ -1948,12 +1950,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST; final int type; + final int receiverProcessState; if (queue == null) { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN; + receiverProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; } else if (queue.getActiveViaColdStart()) { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; + receiverProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; } else { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM; + receiverProcessState = queue.lastProcessState; } // With the new per-process queues, there's no delay between being // "dispatched" and "scheduled", so we report no "receive delay" @@ -1967,7 +1973,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue { FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName, receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState, app != null ? app.info.packageName : null, r.callerPackage, - r.calculateTypeForLogging(), r.getDeliveryGroupPolicy()); + r.calculateTypeForLogging(), r.getDeliveryGroupPolicy(), r.intent.getFlags(), + BroadcastRecord.getReceiverPriority(receiver), r.callerProcState, + receiverProcessState); } } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index cfdb13393e80..198adcb7606a 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -44,6 +44,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; +import android.app.ActivityManager.ProcessState; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.BackgroundStartPrivileges; @@ -93,6 +94,7 @@ final class BroadcastRecord extends Binder { final @Nullable String callerFeatureId; // which feature in the package sent this final int callingPid; // the pid of who sent this final int callingUid; // the uid of who sent this + final @ProcessState int callerProcState; // Procstate of the caller process at enqueue time. final int originalStickyCallingUid; // if this is a sticky broadcast, the Uid of the original sender @@ -268,6 +270,8 @@ final class BroadcastRecord extends Binder { BroadcastFilter curFilter; // the registered receiver currently running. Bundle curFilteredExtras; // the bundle that has been filtered by the package visibility rules + int curAppLastProcessState; // The last process state of the current receiver before receiving + boolean mIsReceiverAppRunning; // Was the receiver's app already running. boolean mWasReceiverAppStopped; // Was the receiver app stopped prior to starting @@ -429,13 +433,14 @@ final class BroadcastRecord extends Binder { boolean initialSticky, int userId, @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, - @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) { + @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, + int callerAppProcessState) { this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, serialized, sticky, initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver); + filterExtrasForReceiver, callerAppProcessState); } BroadcastRecord(BroadcastQueue _queue, @@ -450,7 +455,8 @@ final class BroadcastRecord extends Binder { boolean _initialSticky, int _userId, int originalStickyCallingUid, @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, - @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) { + @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, + int callerAppProcessState) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -462,6 +468,7 @@ final class BroadcastRecord extends Binder { callerFeatureId = _callerFeatureId; callingPid = _callingPid; callingUid = _callingUid; + callerProcState = callerAppProcessState; callerInstantApp = _callerInstantApp; callerInstrumented = isCallerInstrumented(_callerApp, _callingUid); resolvedType = _resolvedType; @@ -515,6 +522,7 @@ final class BroadcastRecord extends Binder { callerFeatureId = from.callerFeatureId; callingPid = from.callingPid; callingUid = from.callingUid; + callerProcState = from.callerProcState; callerInstantApp = from.callerInstantApp; callerInstrumented = from.callerInstrumented; ordered = from.ordered; @@ -600,7 +608,8 @@ final class BroadcastRecord extends Binder { requiredPermissions, excludedPermissions, excludedPackages, appOp, options, splitReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, - mBackgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + mBackgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerProcState); split.enqueueTime = this.enqueueTime; split.enqueueRealTime = this.enqueueRealTime; split.enqueueClockTime = this.enqueueClockTime; @@ -680,7 +689,7 @@ final class BroadcastRecord extends Binder { uid2receiverList.valueAt(i), null /* _resultToApp */, null /* _resultTo */, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, mBackgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver); + filterExtrasForReceiver, callerProcState); br.enqueueTime = this.enqueueTime; br.enqueueRealTime = this.enqueueRealTime; br.enqueueClockTime = this.enqueueClockTime; diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index e744eee8b485..3f7d8ba1a120 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -246,9 +246,13 @@ public class ContentProviderHelper { } } + final int callingProcessState = r != null + ? r.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + if (providerRunning) { cpi = cpr.info; + if (r != null && cpr.canRunHere(r)) { checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, cpr.name.flattenToShortString(), startTime); @@ -266,7 +270,8 @@ public class ContentProviderHelper { r.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, callingProcessState); return holder; } @@ -282,6 +287,8 @@ public class ContentProviderHelper { checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, cpr.name.flattenToShortString(), startTime); + final int providerProcessState = cpr.proc.mState.getCurProcState(); + final long origId = Binder.clearCallingIdentity(); try { checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); @@ -338,7 +345,8 @@ public class ContentProviderHelper { cpr.proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, providerProcessState); } } finally { Binder.restoreCallingIdentity(origId); @@ -516,7 +524,8 @@ public class ContentProviderHelper { proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, proc.mState.getCurProcState()); } else { final int packageState = ((cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) @@ -541,7 +550,8 @@ public class ContentProviderHelper { PROVIDER_ACQUISITION_EVENT_REPORTED, proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD, - packageState, cpi.packageName, callingPackage); + packageState, cpi.packageName, callingPackage, + callingProcessState, ActivityManager.PROCESS_STATE_NONEXISTENT); } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a86c2e362c54..764bbe8bd191 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1471,7 +1471,8 @@ public class OomAdjuster { if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState()) || uidRec.isSetAllowListed()) { uidRec.setLastBackgroundTime(nowElapsed); - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { // Note: the background settle time is in elapsed realtime, while // the handler time base is uptime. All this means is that we may // stop background uids later than we had intended, but that only @@ -3227,7 +3228,8 @@ public class OomAdjuster { // (for states debouncing to avoid from thrashing). state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed); // Kick off the delayed checkup message if needed. - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs); } @@ -3346,6 +3348,7 @@ public class OomAdjuster { @GuardedBy("mService") void idleUidsLocked() { final int N = mActiveUids.size(); + mService.mHandler.removeMessages(IDLE_UIDS_MSG); if (N <= 0) { return; } @@ -3391,7 +3394,6 @@ public class OomAdjuster { } } if (nextTime > 0) { - mService.mHandler.removeMessages(IDLE_UIDS_MSG); mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4342cb994754..c5776d822c8f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2572,9 +2572,14 @@ public final class ProcessList { // and did the cleanup before the actual death notification. Check the dying processes. predecessor = mDyingProcesses.get(processName, info.uid); if (predecessor != null) { - if (app != null) { + // The process record could have existed but its pid is set to 0. In this case, + // the 'app' and 'predecessor' could end up pointing to the same instance; + // so make sure we check this case here. + if (app != null && app != predecessor) { app.mPredecessor = predecessor; predecessor.mSuccessor = app; + } else { + app = null; } Slog.w(TAG_PROCESSES, predecessor.toString() + " is attached to a previous process " + predecessor.getDyingPid()); @@ -5195,6 +5200,8 @@ public final class ProcessList { mDyingProcesses.remove(app.processName, app.uid); app.setDyingPid(0); handlePrecedingAppDiedLocked(app); + // Remove from the LRU list if it's still there. + removeLruProcessLocked(app); return true; } return false; @@ -5243,7 +5250,9 @@ public final class ProcessList { mAppsInBackgroundRestricted.add(app); final long future = killAppIfBgRestrictedAndCachedIdleLocked( app, nowElapsed); - if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (future > 0 + && (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG))) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, future - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d6495c78574c..267d2464e0d5 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -669,6 +669,11 @@ class ProcessRecord implements WindowProcessListener { return mOnewayThread; } + @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getCurProcState() { + return mState.getCurProcState(); + } + @GuardedBy({"mService", "mProcLock"}) public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { mProfile.onProcessActive(thread, tracker); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index dccbb0accadd..50fe6d71d26e 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE; @@ -244,6 +245,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN */ long mRestartSchedulingTime; + /** + * The snapshot process state when the service is requested (either start or bind). + */ + int mProcessStateOnRequest; + static class StartItem { final ServiceRecord sr; final boolean taskRemoved; @@ -253,6 +259,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN final Intent intent; final NeededUriGrants neededGrants; final @Nullable String mCallingPackageName; + final int mCallingProcessState; long deliveredTime; int deliveryCount; int doneExecutingCount; @@ -262,7 +269,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, NeededUriGrants _neededGrants, int _callingId, - String callingProcessName, @Nullable String callingPackageName) { + String callingProcessName, @Nullable String callingPackageName, + int callingProcessState) { sr = _sr; taskRemoved = _taskRemoved; id = _id; @@ -271,6 +279,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN callingId = _callingId; mCallingProcessName = callingProcessName; mCallingPackageName = callingPackageName; + mCallingProcessState = callingProcessState; } UriPermissionOwner getUriPermissionsLocked() { @@ -873,6 +882,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN app.mServices.updateHostingComonentTypeForBindingsLocked(); } app = proc; + updateProcessStateOnRequest(); if (pendingConnectionGroup > 0 && proc != null) { final ProcessServiceRecord psr = proc.mServices; psr.setConnectionService(this); @@ -899,6 +909,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } + void updateProcessStateOnRequest() { + mProcessStateOnRequest = app != null && app.getThread() != null && !app.isKilled() + ? app.mState.getCurProcState() : PROCESS_STATE_NONEXISTENT; + } + @NonNull ArrayMap<IBinder, ArrayList<ConnectionRecord>> getConnections() { return connections; @@ -1061,12 +1076,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (app == null) { return; } - if (mBackgroundStartPrivilegesByStartMerged.allowsAny() - || mIsAllowedBgActivityStartsByBinding) { + BackgroundStartPrivileges backgroundStartPrivileges = + getBackgroundStartPrivilegesWithExclusiveToken(); + if (backgroundStartPrivileges.allowsAny()) { // if the token is already there it's safe to "re-add it" - we're dealing with // a set of Binder objects app.addOrUpdateBackgroundStartPrivileges(this, - getBackgroundStartPrivilegesWithExclusiveToken()); + backgroundStartPrivileges); } else { app.removeBackgroundStartPrivileges(this); } diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index a6677a5185ca..7eeec32cf24a 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -104,40 +105,62 @@ public class UidObserverController { } } - void addUidToObserver(@NonNull IBinder observerToken, int uid) { - synchronized (mLock) { - int i = mUidObservers.beginBroadcast(); - while (i-- > 0) { - var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - if (reg.getToken().equals(observerToken)) { - reg.addUid(uid); - break; - } + final void addUidToObserver(@NonNull IBinder observerToken, int uid) { + Message msg = Message.obtain(mHandler, ActivityManagerService.ADD_UID_TO_OBSERVER_MSG, + uid, /*arg2*/ 0, observerToken); + mHandler.sendMessage(msg); + } - if (i == 0) { - Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); - } + /** + * Add a uid to the list of uids an observer is interested in. Must be run on the same thread + * as mDispatchRunnable. + * + * @param observerToken The token identifier for a UidObserver + * @param uid The uid to add to the list of watched uids + */ + public final void addUidToObserverImpl(@NonNull IBinder observerToken, int uid) { + int i = mUidObservers.beginBroadcast(); + while (i-- > 0) { + var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); + if (reg.getToken().equals(observerToken)) { + reg.addUid(uid); + break; + } + + if (i == 0) { + Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } - mUidObservers.finishBroadcast(); } + mUidObservers.finishBroadcast(); } - void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { - synchronized (mLock) { - int i = mUidObservers.beginBroadcast(); - while (i-- > 0) { - var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - if (reg.getToken().equals(observerToken)) { - reg.removeUid(uid); - break; - } + final void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { + Message msg = Message.obtain(mHandler, ActivityManagerService.REMOVE_UID_FROM_OBSERVER_MSG, + uid, /*arg2*/ 0, observerToken); + mHandler.sendMessage(msg); + } - if (i == 0) { - Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); - } + /** + * Remove a uid from the list of uids an observer is interested in. Must be run on the same + * thread as mDispatchRunnable. + * + * @param observerToken The token identifier for a UidObserver + * @param uid The uid to remove from the list of watched uids + */ + public final void removeUidFromObserverImpl(@NonNull IBinder observerToken, int uid) { + int i = mUidObservers.beginBroadcast(); + while (i-- > 0) { + var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); + if (reg.getToken().equals(observerToken)) { + reg.removeUid(uid); + break; + } + + if (i == 0) { + Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } - mUidObservers.finishBroadcast(); } + mUidObservers.finishBroadcast(); } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 1f8ff73b2d5d..fc72a771fa9c 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -2214,6 +2214,7 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.removePreferredDevicesForStrategyInt(mAccessibilityStrategyId); } mDeviceInventory.applyConnectedDevicesRoles(); + mDeviceInventory.reapplyExternalDevicesRoles(); } else { mDeviceInventory.setPreferredDevicesForStrategyInt( mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice)); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index d1cae490a31d..219dda398ebf 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -361,23 +361,34 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } - mAppliedStrategyRoles.clear(); mAppliedStrategyRolesInt.clear(); - mAppliedPresetRoles.clear(); mAppliedPresetRolesInt.clear(); applyConnectedDevicesRoles_l(); } + reapplyExternalDevicesRoles(); + } + + /*package*/ void reapplyExternalDevicesRoles() { + synchronized (mDevicesLock) { + mAppliedStrategyRoles.clear(); + mAppliedPresetRoles.clear(); + } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, devices) -> { - setPreferredDevicesForStrategy(strategy, devices); }); + setPreferredDevicesForStrategy(strategy, devices); + }); } synchronized (mNonDefaultDevices) { mNonDefaultDevices.forEach((strategy, devices) -> { addDevicesRoleForStrategy(strategy, AudioSystem.DEVICE_ROLE_DISABLED, - devices, false /* internal */); }); + devices, false /* internal */); + }); } synchronized (mPreferredDevicesForCapturePreset) { - // TODO: call audiosystem to restore + mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { + setDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + }); } } @@ -1163,6 +1174,7 @@ public class AudioDeviceInventory { return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); }); purgeRoles(mAppliedPresetRolesInt, (p, r, d) -> { return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }); + reapplyExternalDevicesRoles(); } @GuardedBy("mDevicesLock") diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d0b6cdce037f..ac03c82260f9 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1368,6 +1368,7 @@ public class AudioService extends IAudioService.Stub intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); intentFilter.addAction(ACTION_CHECK_MUSIC_ACTIVE); + intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null, Context.RECEIVER_EXPORTED); @@ -3277,6 +3278,7 @@ public class AudioService extends IAudioService.Stub if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1 streamType = mVolumeControlStream; } else { + // TODO discard activity on a muted stream? final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType); final boolean activeForReal; if (maybeActiveStreamType == AudioSystem.STREAM_RING @@ -3480,9 +3482,10 @@ public class AudioService extends IAudioService.Stub } } else if (isStreamMutedByRingerOrZenMode(streamTypeAlias) && streamState.mIsMuted) { // if the stream is currently muted streams by ringer/zen mode - // then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) + // then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) with an unmute or raise if (direction == AudioManager.ADJUST_TOGGLE_MUTE - || direction == AudioManager.ADJUST_UNMUTE) { + || direction == AudioManager.ADJUST_UNMUTE + || direction == AudioManager.ADJUST_RAISE) { adjustVolume = false; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index aa5f9fa96875..7fa4d6ce4d49 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -573,7 +573,7 @@ public class BiometricScheduler { final BiometricSchedulerOperation operation = mCurrentOperation; mHandler.postDelayed(() -> { if (operation == mCurrentOperation) { - Counter.logIncrement("biometric.scheduler_watchdog_triggered_count"); + Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count"); clearScheduler(); } }, 10000); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index aeff2b0f9af6..d29d9c84501d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -119,6 +119,7 @@ import static android.service.notification.NotificationListenerService.TRIM_LIGH import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -223,6 +224,8 @@ import android.os.IInterface; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -234,6 +237,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.VibrationEffect; +import android.os.WorkSource; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.Settings; @@ -559,6 +563,7 @@ public class NotificationManagerService extends SystemService { private PermissionHelper mPermissionHelper; private UsageStatsManagerInternal mUsageStatsManagerInternal; private TelecomManager mTelecomManager; + private PowerManager mPowerManager; private PostNotificationTrackerFactory mPostNotificationTrackerFactory; final IBinder mForegroundToken = new Binder(); @@ -923,7 +928,7 @@ public class NotificationManagerService extends SystemService { if (oldFlags != flags) { summary.getSbn().getNotification().flags = flags; mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } @@ -1457,7 +1462,7 @@ public class NotificationManagerService extends SystemService { // want to adjust the flag behaviour. mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r, true /* isAppForeground*/, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -1488,7 +1493,7 @@ public class NotificationManagerService extends SystemService { mHandler.post( new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r, /* foreground= */ true, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -2233,7 +2238,7 @@ public class NotificationManagerService extends SystemService { UsageStatsManagerInternal usageStatsManagerInternal, TelecomManager telecomManager, NotificationChannelLogger channelLogger, SystemUiSystemPropertiesFlags.FlagResolver flagResolver, - PermissionManager permissionManager, + PermissionManager permissionManager, PowerManager powerManager, PostNotificationTrackerFactory postNotificationTrackerFactory) { mHandler = handler; Resources resources = getContext().getResources(); @@ -2265,6 +2270,7 @@ public class NotificationManagerService extends SystemService { mDpm = dpm; mUm = userManager; mTelecomManager = telecomManager; + mPowerManager = powerManager; mPostNotificationTrackerFactory = postNotificationTrackerFactory; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2568,6 +2574,7 @@ public class NotificationManagerService extends SystemService { getContext().getSystemService(TelecomManager.class), new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver(), getContext().getSystemService(PermissionManager.class), + getContext().getSystemService(PowerManager.class), new PostNotificationTrackerFactory() {}); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, @@ -2684,7 +2691,7 @@ public class NotificationManagerService extends SystemService { final boolean isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } @@ -6577,7 +6584,7 @@ public class NotificationManagerService extends SystemService { void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, final int callingPid, final String tag, final int id, final Notification notification, int incomingUserId, boolean postSilently) { - PostNotificationTracker tracker = mPostNotificationTrackerFactory.newTracker(); + PostNotificationTracker tracker = acquireWakeLockForPost(pkg, callingUid); boolean enqueued = false; try { enqueued = enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, @@ -6589,6 +6596,25 @@ public class NotificationManagerService extends SystemService { } } + private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) { + if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION) + && Binder.withCleanCallingIdentity( + () -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, false))) { + // The package probably doesn't have WAKE_LOCK permission and should not require it. + return Binder.withCleanCallingIdentity(() -> { + WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "NotificationManagerService:post:" + pkg); + wakeLock.setWorkSource(new WorkSource(uid, pkg)); + // TODO(b/275044361): Adjust to a more reasonable number when we have the data. + wakeLock.acquire(30_000); + return mPostNotificationTrackerFactory.newTracker(wakeLock); + }); + } else { + return mPostNotificationTrackerFactory.newTracker(null); + } + } + /** * @return True if we successfully processed the notification and handed off the task of * enqueueing it to a background thread; false otherwise. @@ -7106,7 +7132,7 @@ public class NotificationManagerService extends SystemService { mHandler.post( new NotificationManagerService.EnqueueNotificationRunnable( r.getUser().getIdentifier(), r, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -12168,20 +12194,20 @@ public class NotificationManagerService extends SystemService { } interface PostNotificationTrackerFactory { - default PostNotificationTracker newTracker() { - return new PostNotificationTracker(); + default PostNotificationTracker newTracker(@Nullable WakeLock optionalWakelock) { + return new PostNotificationTracker(optionalWakelock); } } static class PostNotificationTracker { @ElapsedRealtimeLong private final long mStartTime; - @Nullable private NotificationRecordLogger.NotificationReported mReport; + @Nullable private final WakeLock mWakeLock; private boolean mOngoing; @VisibleForTesting - PostNotificationTracker() { - // TODO(b/275044361): (Conditionally) receive a wakelock. + PostNotificationTracker(@Nullable WakeLock wakeLock) { mStartTime = SystemClock.elapsedRealtime(); + mWakeLock = wakeLock; mOngoing = true; if (DBG) { Slog.d(TAG, "PostNotification: Started"); @@ -12199,9 +12225,8 @@ public class NotificationManagerService extends SystemService { } /** - * Cancels the tracker (TODO(b/275044361): releasing the acquired WakeLock). Either - * {@link #finish} or {@link #cancel} (exclusively) should be called on this object before - * it's discarded. + * Cancels the tracker (releasing the acquired WakeLock). Either {@link #finish} or + * {@link #cancel} (exclusively) should be called on this object before it's discarded. */ void cancel() { if (!isOngoing()) { @@ -12209,9 +12234,9 @@ public class NotificationManagerService extends SystemService { return; } mOngoing = false; - - // TODO(b/275044361): Release wakelock. - + if (mWakeLock != null) { + Binder.withCleanCallingIdentity(() -> mWakeLock.release()); + } if (DBG) { long elapsedTime = SystemClock.elapsedRealtime() - mStartTime; Slog.d(TAG, TextUtils.formatSimple("PostNotification: Abandoned after %d ms", @@ -12220,9 +12245,9 @@ public class NotificationManagerService extends SystemService { } /** - * Finishes the tracker (TODO(b/275044361): releasing the acquired WakeLock) and returns the - * time elapsed since the operation started, in milliseconds. Either {@link #finish} or - * {@link #cancel} (exclusively) should be called on this object before it's discarded. + * Finishes the tracker (releasing the acquired WakeLock) and returns the time elapsed since + * the operation started, in milliseconds. Either {@link #finish} or {@link #cancel} + * (exclusively) should be called on this object before it's discarded. */ @DurationMillisLong long finish() { @@ -12232,9 +12257,9 @@ public class NotificationManagerService extends SystemService { return elapsedTime; } mOngoing = false; - - // TODO(b/275044361): Release wakelock. - + if (mWakeLock != null) { + Binder.withCleanCallingIdentity(() -> mWakeLock.release()); + } if (DBG) { Slog.d(TAG, TextUtils.formatSimple("PostNotification: Finished in %d ms", elapsedTime)); diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING new file mode 100644 index 000000000000..59b2bc1e4f73 --- /dev/null +++ b/services/core/java/com/android/server/notification/TEST_MAPPING @@ -0,0 +1,49 @@ +{ + "presubmit": [ + { + "name": "CtsNotificationTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + }, + { + "name": "FrameworksUiServicesTests", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsNotificationTestCases" + } + ] +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 6491fd1b1f98..a9115371413c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -511,7 +511,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } catch (FileNotFoundException e) { // Missing sessions are okay, probably first boot - } catch (IOException | XmlPullParserException e) { + } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) { Slog.wtf(TAG, "Failed reading install sessions", e); } finally { IoUtils.closeQuietly(fis); diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java index 19aa4f8e8d0b..54ca426a6dc3 100644 --- a/services/core/java/com/android/server/pm/ResilientAtomicFile.java +++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java @@ -230,7 +230,9 @@ final class ResilientAtomicFile implements Closeable { + Log.getStackTraceString(e)); } - mCurrentFile.delete(); + if (!mCurrentFile.delete()) { + throw new IllegalStateException("Failed to remove " + mCurrentFile); + } mCurrentFile = null; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f2d1357ac1b5..aaf13ebeff2a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3714,11 +3714,16 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (parser.getName().equals(TAG_PERMISSIONS)) { final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = getSettingLPr(ps.getSharedUserAppId()).getLegacyPermissionState(); + final SettingBase sharedUserSettings = getSettingLPr( + ps.getSharedUserAppId()); + legacyState = sharedUserSettings != null + ? sharedUserSettings.getLegacyPermissionState() : null; } else { legacyState = ps.getLegacyPermissionState(); } - readInstallPermissionsLPr(parser, legacyState, users); + if (legacyState != null) { + readInstallPermissionsLPr(parser, legacyState, users); + } } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) { readUsesStaticLibLPw(parser, ps); } else if (parser.getName().equals(TAG_USES_SDK_LIB)) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index a020728cc85e..5b3514c01f9f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2547,10 +2547,7 @@ public class ShortcutService extends IShortcutService.Stub { enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, "getShareTargets"); final ComponentName chooser = injectChooserActivity(); - final String pkg = (chooser != null - && mPackageManagerInternal.getComponentEnabledSetting(chooser, - injectBinderCallingUid(), userId) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) - ? chooser.getPackageName() : mContext.getPackageName(); + final String pkg = chooser != null ? chooser.getPackageName() : mContext.getPackageName(); synchronized (mLock) { throwIfUserLockedL(userId); final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>(); 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 d108e1487564..d55f85cde5af 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -986,11 +986,14 @@ public class PackageInfoUtils { } /** @see ApplicationInfo#privateFlagsExt */ - public static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags, + private static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags, @Nullable PackageStateInternal pkgSetting) { // @formatter:off - // TODO: Add state specific flags - return pkgWithoutStateFlags; + int flags = pkgWithoutStateFlags; + if (pkgSetting != null) { + flags |= flag(pkgSetting.getCpuAbiOverride() != null, ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE); + } + return flags; // @formatter:on } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 3e7ae331d705..2499529f0fc0 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -141,6 +141,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ParsedPermission; import com.android.server.pm.pkg.component.ParsedPermissionGroup; @@ -4538,8 +4539,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt final int appId = ps.getAppId(); final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = mPackageManagerInt.getSharedUserApi( - ps.getSharedUserAppId()).getSharedUserLegacyPermissionState(); + final int sharedUserId = ps.getSharedUserAppId(); + SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId); + if (sharedUserApi == null) { + Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId); + return; + } + legacyState = sharedUserApi.getSharedUserLegacyPermissionState(); } else { legacyState = ps.getLegacyPermissionState(); } @@ -4584,8 +4590,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt ps.setInstallPermissionsFixed(false); final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = mPackageManagerInt.getSharedUserApi( - ps.getSharedUserAppId()).getSharedUserLegacyPermissionState(); + final int sharedUserId = ps.getSharedUserAppId(); + SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId); + if (sharedUserApi == null) { + Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId); + return; + } + legacyState = sharedUserApi.getSharedUserLegacyPermissionState(); } else { legacyState = ps.getLegacyPermissionState(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java index 9ff6a0d7e6ee..d87fca4d3c71 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperData.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java @@ -133,16 +133,14 @@ class WallpaperData { */ final Rect cropHint = new Rect(0, 0, 0, 0); - WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) { - this.userId = userId; - wallpaperFile = new File(wallpaperDir, inputFileName); - cropFile = new File(wallpaperDir, cropFileName); - } - WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) { - this(userId, getWallpaperDir(userId), - (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER, - (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP); + this.userId = userId; + this.mWhich = wallpaperType; + File wallpaperDir = getWallpaperDir(userId); + String wallpaperFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER; + String cropFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP; + this.wallpaperFile = new File(wallpaperDir, wallpaperFileName); + this.cropFile = new File(wallpaperDir, cropFileName); } /** diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index a079875a23e4..9e9b3444006c 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1588,7 +1588,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mShuttingDown = false; mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); - mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context); + mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mIPackageManager = AppGlobals.getPackageManager(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2b2100e56f44..9f16a8441533 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4281,6 +4281,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this); mTaskSupervisor.mStoppingActivities.remove(this); + mLetterboxUiController.destroy(); waitingToShow = false; // Defer removal of this activity when either a child is animating, or app transition is on @@ -4350,8 +4351,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); } - mLetterboxUiController.destroy(); - if (!delayed) { updateReportedVisibilityLocked(); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0ebec11ea36f..a0a45e67e77b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1573,8 +1573,10 @@ class ActivityStarter { // existence change. transitionController.collectExistenceChange(started); } else if (result == START_DELIVERED_TO_TOP && newTransition != null - // An activity has changed order/visibility so this isn't just deliver-to-top - && mMovedToTopActivity == null) { + // An activity has changed order/visibility or the task is occluded by a transient + // activity, so this isn't just deliver-to-top + && mMovedToTopActivity == null + && !transitionController.isTransientHide(startedActivityRootTask)) { // We just delivered to top, so there isn't an actual transition here. if (!forceTransientTransition) { newTransition.abort(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a650d7709b67..47b51ac164b9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2012,7 +2012,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); if (dc == null) return; - final Task task = dc.getTask((t) -> t.isLeafTask() && t.isFocusable(), + final Task task = dc.getTask((t) -> t.isLeafTask() && t.isTopActivityFocusable(), true /* traverseTopToBottom */); if (task == null) return; setFocusedTask(task.mTaskId, null /* touchedActivity */); @@ -4505,8 +4505,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // to set debug system properties. To ensure that system properties are set // only when allowed, we check the current UID. if (Process.myUid() == Process.SYSTEM_UID) { - SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc)); - SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc)); + if (values.mcc != 0) { + SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc)); + } + if (values.mnc != 0) { + SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc)); + } } if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index 123a74dbf597..f7ccc0d91969 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Build; import android.os.Handler; @@ -178,6 +179,14 @@ class AppWarnings { * @param r activity record for which the warning may be displayed */ public void showDeprecatedAbiDialogIfNeeded(ActivityRecord r) { + final boolean isUsingAbiOverride = (r.info.applicationInfo.privateFlagsExt + & ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE) != 0; + if (isUsingAbiOverride) { + // The abiOverride flag was specified during installation, which means that if the app + // is currently running in 32-bit mode, it is intended. Do not show the warning dialog. + return; + } + // The warning dialog can also be disabled for debugging purpose final boolean disableDeprecatedAbiDialog = SystemProperties.getBoolean( "debug.wm.disable_deprecated_abi_dialog", false); if (disableDeprecatedAbiDialog) { diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 7e783938d30a..01158779c24f 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -185,6 +185,8 @@ class AsyncRotationController extends FadeAnimationController implements Consume } } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS) { action = Operation.ACTION_SEAMLESS; + } else if (mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) { + return; } mTargetWindowTokens.put(w.mToken, new Operation(action)); return; diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 1fbf59301191..2b34bb22729d 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -42,6 +42,7 @@ import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.pm.ActivityInfo.ScreenOrientation; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.hardware.camera2.CameraManager; import android.os.Handler; @@ -423,7 +424,18 @@ final class DisplayRotationCompatPolicy { // for the activity embedding case. if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW && isTreatmentEnabledForActivity(topActivity, /* mustBeFullscreen */ false)) { - showToast(R.string.display_rotation_camera_compat_toast_in_split_screen); + final PackageManager packageManager = mWmService.mContext.getPackageManager(); + try { + showToast( + R.string.display_rotation_camera_compat_toast_in_multi_window, + (String) packageManager.getApplicationLabel( + packageManager.getApplicationInfo(packageName, /* flags */ 0))); + } catch (PackageManager.NameNotFoundException e) { + ProtoLog.e(WM_DEBUG_ORIENTATION, + "DisplayRotationCompatPolicy: Multi-window toast not shown as " + + "package '%s' cannot be found.", + packageName); + } } } } @@ -434,6 +446,15 @@ final class DisplayRotationCompatPolicy { () -> Toast.makeText(mWmService.mContext, stringRes, Toast.LENGTH_LONG).show()); } + @VisibleForTesting + void showToast(@StringRes int stringRes, @NonNull String applicationLabel) { + UiThread.getHandler().post( + () -> Toast.makeText( + mWmService.mContext, + mWmService.mContext.getString(stringRes, applicationLabel), + Toast.LENGTH_LONG).show()); + } + private synchronized void notifyCameraClosed(@NonNull String cameraId) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that Camera %s is closed, scheduling rotation update.", diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 1a322ff0196f..5e2618b00e2e 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -454,7 +454,7 @@ class InsetsSourceProvider { if (mSource.getType() == WindowInsets.Type.ime()) { setClientVisible(target.isRequestedVisible(WindowInsets.Type.ime())); } - final Transaction t = mDisplayContent.getSyncTransaction(); + final Transaction t = mWindowContainer.getSyncTransaction(); mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */, ANIMATION_TYPE_INSETS_CONTROL); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 4995236da1be..bc7fa31125f9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2357,10 +2357,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */, display.mTransitionController, mWmService.mSyncEngine); final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> { - display.mTransitionController.requestStartTransition(transition, - null /* trigger */, null /* remote */, null /* display */); - // Force playing immediately so that unrelated ops can't be collected. - transition.playNow(); + if (deferred && !display.shouldSleep()) { + transition.abort(); + } else { + display.mTransitionController.requestStartTransition(transition, + null /* trigger */, null /* remote */, null /* display */); + // Force playing immediately so that unrelated ops can't be collected. + transition.playNow(); + } }; if (!display.mTransitionController.isCollecting()) { // Since this bypasses sync, submit directly ignoring whether sync-engine diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index bb6f8056acda..7104739068e4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3290,6 +3290,12 @@ class Task extends TaskFragment { scheduleAnimation(); } + // Let organizer manage task visibility for shell transition. So don't change it's + // visibility during collecting. + if (mTransitionController.isCollecting() && mCreatedByOrganizer) { + return; + } + // We intend to let organizer manage task visibility but it doesn't // have enough information until we finish shell transitions. // In the mean time we do an easy fix here. @@ -5687,17 +5693,28 @@ class Task extends TaskFragment { } private boolean moveTaskToBackInner(@NonNull Task task) { - moveToBack("moveTaskToBackInner", task); - - if (inPinnedWindowingMode()) { - mTaskSupervisor.removeRootTask(this); - return true; + if (mTransitionController.isShellTransitionsEnabled()) { + // Preventing from update surface position for WindowState if configuration changed, + // because the position is depends on WindowFrame, so update the position before + // relayout will only update it to "old" position. + mAtmService.deferWindowLayout(); } + try { + moveToBack("moveTaskToBackInner", task); - mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, - mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */, - false /* deferResume */); + if (inPinnedWindowingMode()) { + mTaskSupervisor.removeRootTask(this); + return true; + } + mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, + mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */, + false /* deferResume */); + } finally { + if (mTransitionController.isShellTransitionsEnabled()) { + mAtmService.continueWindowLayout(); + } + } ActivityRecord topActivity = getDisplayArea().topRunningActivity(); Task topRootTask = topActivity.getRootTask(); if (topRootTask != null && topRootTask != this && topActivity.isState(RESUMED)) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index c6c3b14bf98b..0c1f33ccedbc 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1017,8 +1017,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (isTopActivityLaunchedBehind()) { return TASK_FRAGMENT_VISIBILITY_VISIBLE; } + final WindowContainer<?> parent = getParent(); final Task thisTask = asTask(); - if (thisTask != null && mTransitionController.isTransientHide(thisTask)) { + if (thisTask != null && parent.asTask() == null + && mTransitionController.isTransientHide(thisTask)) { + // Keep transient-hide root tasks visible. Non-root tasks still follow standard rule. return TASK_FRAGMENT_VISIBILITY_VISIBLE; } @@ -1028,7 +1031,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { // This TaskFragment is only considered visible if all its parent TaskFragments are // considered visible, so check the visibility of all ancestor TaskFragment first. - final WindowContainer parent = getParent(); if (parent.asTaskFragment() != null) { final int parentVisibility = parent.asTaskFragment().getVisibility(starting); if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 9a5f766989ca..aad12251502d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -234,9 +234,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { private @TransitionState int mState = STATE_PENDING; private final ReadyTracker mReadyTracker = new ReadyTracker(); - // TODO(b/188595497): remove when not needed. - /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ - private boolean mNavBarAttachedToApp = false; private int mRecentsDisplayId = INVALID_DISPLAY; /** The delay for light bar appearance animation. */ @@ -690,6 +687,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (!wc.mDisplayContent.getDisplayPolicy().isScreenOnFully() || wc.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF) { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; + return; } if (mContainerFreezer == null) { @@ -1190,7 +1188,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // processed all the participants first (in particular, we want to trigger pip-enter first) for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); - if (ar != null) { + // If the activity was just inserted to an invisible task, it will keep INITIALIZING + // state. Then no need to notify the callback to avoid clearing some states + // unexpectedly, e.g. launch-task-behind. + if (ar != null && (ar.isVisibleRequested() + || !ar.isState(ActivityRecord.State.INITIALIZING))) { mController.dispatchLegacyAppTransitionFinished(ar); } } @@ -1776,7 +1778,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (navWindow == null || navWindow.mToken == null) { return; } - mNavBarAttachedToApp = true; + mController.mNavigationBarAttachedToApp = true; navWindow.mToken.cancelAnimation(); final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); @@ -1798,8 +1800,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** @see RecentsAnimationController#restoreNavigationBarFromApp */ void legacyRestoreNavigationBarFromApp() { - if (!mNavBarAttachedToApp) return; - mNavBarAttachedToApp = false; + if (!mController.mNavigationBarAttachedToApp) { + return; + } + mController.mNavigationBarAttachedToApp = false; if (mRecentsDisplayId == INVALID_DISPLAY) { Slog.e(TAG, "Reparented navigation bar without a valid display"); @@ -1832,6 +1836,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { break; } + final AsyncRotationController asyncRotationController = dc.getAsyncRotationController(); + if (asyncRotationController != null) { + asyncRotationController.accept(navWindow); + } + if (animate) { final NavBarFadeAnimationController controller = new NavBarFadeAnimationController(dc); @@ -1840,6 +1849,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Reparent the SurfaceControl of nav bar token back. t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); } + + // To apply transactions. + dc.mWmService.scheduleAnimationLocked(); } private void reportStartReasonsToLogger() { @@ -2244,11 +2256,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, wc); // Make leash based on highest (z-order) direct child of ancestor with a participant. + // Check whether the ancestor is belonged to last parent, shouldn't happen. + final boolean hasReparent = !wc.isDescendantOf(ancestor); WindowContainer leashReference = wc; - while (leashReference.getParent() != ancestor) { - leashReference = leashReference.getParent(); + if (hasReparent) { + Slog.e(TAG, "Did not find common ancestor! Ancestor= " + ancestor + + " target= " + wc); + } else { + while (leashReference.getParent() != ancestor) { + leashReference = leashReference.getParent(); + } } - final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( "Transition Root: " + leashReference.getName()).build(); rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); @@ -2307,23 +2325,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { task.fillTaskInfo(tinfo); change.setTaskInfo(tinfo); change.setRotationAnimation(getTaskRotationAnimation(task)); - final ActivityRecord topMostActivity = task.getTopMostActivity(); - change.setAllowEnterPip(topMostActivity != null - && topMostActivity.checkEnterPictureInPictureAppOpsState()); final ActivityRecord topRunningActivity = task.topRunningActivity(); - if (topRunningActivity != null && task.mDisplayContent != null - // Display won't be rotated for multi window Task, so the fixed rotation - // won't be applied. This can happen when the windowing mode is changed - // before the previous fixed rotation is applied. - && (!task.inMultiWindowMode() || !topRunningActivity.inMultiWindowMode())) { - // If Activity is in fixed rotation, its will be applied with the next rotation, - // when the Task is still in the previous rotation. - final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); - final int activityRotation = topRunningActivity.getWindowConfiguration() - .getDisplayRotation(); - if (taskRotation != activityRotation) { - change.setEndFixedRotation(activityRotation); + if (topRunningActivity != null) { + if (topRunningActivity.info.supportsPictureInPicture()) { + change.setAllowEnterPip( + topRunningActivity.checkEnterPictureInPictureAppOpsState()); } + setEndFixedRotationIfNeeded(change, task, topRunningActivity); } } else if ((info.mFlags & ChangeInfo.FLAG_SEAMLESS_ROTATION) != 0) { change.setRotationAnimation(ROTATION_ANIMATION_SEAMLESS); @@ -2431,6 +2439,48 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } return animOptions; } + + private static void setEndFixedRotationIfNeeded(@NonNull TransitionInfo.Change change, + @NonNull Task task, @NonNull ActivityRecord taskTopRunning) { + if (!taskTopRunning.isVisibleRequested()) { + // Fixed rotation only applies to opening or changing activity. + return; + } + if (task.inMultiWindowMode() && taskTopRunning.inMultiWindowMode()) { + // Display won't be rotated for multi window Task, so the fixed rotation won't be + // applied. This can happen when the windowing mode is changed before the previous + // fixed rotation is applied. Check both task and activity because the activity keeps + // fullscreen mode when the task is entering PiP. + return; + } + final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); + final int activityRotation = taskTopRunning.getWindowConfiguration() + .getDisplayRotation(); + // If the Activity uses fixed rotation, its rotation will be applied to display after + // the current transition is done, while the Task is still in the previous rotation. + if (taskRotation != activityRotation) { + change.setEndFixedRotation(activityRotation); + return; + } + + // For example, the task is entering PiP so it no longer decides orientation. If the next + // orientation source (it could be an activity which was behind the PiP or launching to top) + // will change display rotation, then set the fixed rotation hint as well so the animation + // can consider the rotated position. + if (!task.inPinnedWindowingMode() || taskTopRunning.mDisplayContent.inTransition()) { + return; + } + final WindowContainer<?> orientationSource = + taskTopRunning.mDisplayContent.getLastOrientationSource(); + if (orientationSource == null) { + return; + } + final int nextRotation = orientationSource.getWindowConfiguration().getDisplayRotation(); + if (taskRotation != nextRotation) { + change.setEndFixedRotation(nextRotation); + } + } + /** * Finds the top-most common ancestor of app targets. * @@ -2453,6 +2503,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Skip the non-app window or windows on a different display continue; } + // Re-initiate the last parent as the initial ancestor instead of the top target. + // When move a leaf task from organized task to display area, try to keep the transition + // root be the original organized task for close transition animation. + // Otherwise, shell will use wrong root layer to play animation. + // Note: Since the target is sorted, so only need to do this at the lowest target. + if (change.mStartParent != null && wc.getParent() != null + && change.mStartParent.isAttached() && wc.getParent() != change.mStartParent + && i == targets.size() - 1) { + final int transitionMode = change.getTransitMode(wc); + if (transitionMode == TRANSIT_CLOSE || transitionMode == TRANSIT_TO_BACK) { + ancestor = change.mStartParent; + continue; + } + } while (!wc.isDescendantOf(ancestor)) { ancestor = ancestor.getParent(); } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 0cb6f14b38f2..359b353ba336 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -206,6 +206,11 @@ class TransitionController { */ boolean mBuildingFinishLayers = false; + /** + * Whether the surface of navigation bar token is reparented to an app. + */ + boolean mNavigationBarAttachedToApp = false; + private boolean mAnimatingState = false; final Handler mLoggerHandler = FgThread.getHandler(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d84c85c8e823..67572bfcad18 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -88,6 +88,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.fixScale; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; @@ -3591,6 +3592,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setCurrentUser(@UserIdInt int newUserId) { synchronized (mGlobalLock) { + mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_OPEN, null); mCurrentUserId = newUserId; mPolicy.setCurrentUserLw(newUserId); mKeyguardDisableHandler.setCurrentUser(newUserId); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index c918fb87154f..9c1d765fe0f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -101,7 +101,7 @@ final class DevicePolicyEngine { private final UserManager mUserManager; // TODO(b/256849338): add more granular locks - private final Object mLock = new Object(); + private final Object mLock; /** * Map of <userId, Map<policyKey, policyState>> @@ -122,9 +122,11 @@ final class DevicePolicyEngine { DevicePolicyEngine( @NonNull Context context, - @NonNull DeviceAdminServiceController deviceAdminServiceController) { + @NonNull DeviceAdminServiceController deviceAdminServiceController, + @NonNull Object lock) { mContext = Objects.requireNonNull(context); mDeviceAdminServiceController = Objects.requireNonNull(deviceAdminServiceController); + mLock = Objects.requireNonNull(lock); mUserManager = mContext.getSystemService(UserManager.class); mLocalPolicies = new SparseArray<>(); mGlobalPolicies = new HashMap<>(); @@ -152,8 +154,8 @@ final class DevicePolicyEngine { PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); if (policyDefinition.isNonCoexistablePolicy()) { - setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin, - value, userId, skipEnforcePolicy); + setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState, + enforcingAdmin, value, userId, skipEnforcePolicy); return; } @@ -173,7 +175,7 @@ final class DevicePolicyEngine { // the data structures. if (!skipEnforcePolicy) { if (policyChanged) { - onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); + onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } boolean policyEnforced = Objects.equals( localPolicyState.getCurrentResolvedPolicy(), value); @@ -211,7 +213,7 @@ final class DevicePolicyEngine { * * <p>Passing a {@code null} value means the policy set by this admin should be removed. */ - private <V> void setNonCoexistableLocalPolicy( + private <V> void setNonCoexistableLocalPolicyLocked( PolicyDefinition<V> policyDefinition, PolicyState<V> localPolicyState, EnforcingAdmin enforcingAdmin, @@ -266,8 +268,8 @@ final class DevicePolicyEngine { PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); if (policyDefinition.isNonCoexistablePolicy()) { - setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin, - /* value= */ null, userId, /* skipEnforcePolicy= */ false); + setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState, + enforcingAdmin, /* value= */ null, userId, /* skipEnforcePolicy= */ false); return; } @@ -282,7 +284,7 @@ final class DevicePolicyEngine { } if (policyChanged) { - onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); + onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } // For a removePolicy to be enforced, it means no current policy exists @@ -348,7 +350,7 @@ final class DevicePolicyEngine { /** * Enforces the new policy and notifies relevant admins. */ - private <V> void onLocalPolicyChanged( + private <V> void onLocalPolicyChangedLocked( @NonNull PolicyDefinition<V> policyDefinition, @NonNull EnforcingAdmin enforcingAdmin, int userId) { @@ -358,7 +360,7 @@ final class DevicePolicyEngine { policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); // Send policy updates to admins who've set it locally - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( localPolicyState, enforcingAdmin, policyDefinition, @@ -369,7 +371,7 @@ final class DevicePolicyEngine { // Send policy updates to admins who've set it globally if (hasGlobalPolicyLocked(policyDefinition)) { PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( globalPolicyState, enforcingAdmin, policyDefinition, @@ -424,7 +426,7 @@ final class DevicePolicyEngine { // the data structures. if (!skipEnforcePolicy) { if (policyChanged) { - onGlobalPolicyChanged(policyDefinition, enforcingAdmin); + onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } boolean policyAppliedGlobally = Objects.equals( @@ -473,7 +475,7 @@ final class DevicePolicyEngine { boolean policyChanged = policyState.removePolicy(enforcingAdmin); if (policyChanged) { - onGlobalPolicyChanged(policyDefinition, enforcingAdmin); + onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } applyGlobalPolicyOnUsersWithLocalPoliciesLocked(policyDefinition, enforcingAdmin, @@ -499,7 +501,7 @@ final class DevicePolicyEngine { /** * Enforces the new policy globally and notifies relevant admins. */ - private <V> void onGlobalPolicyChanged( + private <V> void onGlobalPolicyChangedLocked( @NonNull PolicyDefinition<V> policyDefinition, @NonNull EnforcingAdmin enforcingAdmin) { PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition); @@ -507,7 +509,7 @@ final class DevicePolicyEngine { enforcePolicy(policyDefinition, policyState.getCurrentResolvedPolicy(), UserHandle.USER_ALL); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( policyState, enforcingAdmin, policyDefinition, @@ -552,7 +554,7 @@ final class DevicePolicyEngine { policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( localPolicyState, enforcingAdmin, policyDefinition, @@ -745,34 +747,35 @@ final class DevicePolicyEngine { } <V> void transferPolicies(EnforcingAdmin oldAdmin, EnforcingAdmin newAdmin) { - Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); - for (PolicyKey policy : globalPolicies) { - PolicyState<?> policyState = mGlobalPolicies.get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { - PolicyDefinition<V> policyDefinition = - (PolicyDefinition<V>) policyState.getPolicyDefinition(); - PolicyValue<V> policyValue = - (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); - setGlobalPolicy(policyDefinition, newAdmin, policyValue); - } - } - - for (int i = 0; i < mLocalPolicies.size(); i++) { - int userId = mLocalPolicies.keyAt(i); - Set<PolicyKey> localPolicies = new HashSet<>( - mLocalPolicies.get(userId).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + synchronized (mLock) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { PolicyDefinition<V> policyDefinition = (PolicyDefinition<V>) policyState.getPolicyDefinition(); PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); - setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + setGlobalPolicy(policyDefinition, newAdmin, policyValue); } } - } + for (int i = 0; i < mLocalPolicies.size(); i++) { + int userId = mLocalPolicies.keyAt(i); + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + } + } + } + } removePoliciesForAdmin(oldAdmin); } @@ -836,7 +839,7 @@ final class DevicePolicyEngine { mLocalPolicies.get(userId).put( policyDefinition.getPolicyKey(), new PolicyState<>(policyDefinition)); } - return getPolicyState(mLocalPolicies.get(userId), policyDefinition); + return getPolicyStateLocked(mLocalPolicies.get(userId), policyDefinition); } private <V> void removeLocalPolicyStateLocked( @@ -858,14 +861,14 @@ final class DevicePolicyEngine { mGlobalPolicies.put( policyDefinition.getPolicyKey(), new PolicyState<>(policyDefinition)); } - return getPolicyState(mGlobalPolicies, policyDefinition); + return getPolicyStateLocked(mGlobalPolicies, policyDefinition); } private <V> void removeGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) { mGlobalPolicies.remove(policyDefinition.getPolicyKey()); } - private static <V> PolicyState<V> getPolicyState( + private static <V> PolicyState<V> getPolicyStateLocked( Map<PolicyKey, PolicyState<?>> policies, PolicyDefinition<V> policyDefinition) { try { // This will not throw an exception because policyDefinition is of type V, so unless @@ -935,7 +938,7 @@ final class DevicePolicyEngine { } // TODO(b/261430877): Finalise the decision on which admins to send the updates to. - private <V> void sendPolicyChangedToAdmins( + private <V> void sendPolicyChangedToAdminsLocked( PolicyState<V> policyState, EnforcingAdmin callingAdmin, PolicyDefinition<V> policyDefinition, @@ -1210,17 +1213,19 @@ final class DevicePolicyEngine { if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) { return; } - if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) { - return; - } - for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get( - parentInfo.getUserHandle().getIdentifier()).entrySet()) { - enforcePolicyOnUser(userId, entry.getValue()); + synchronized (mLock) { + if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) { + return; + } + for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get( + parentInfo.getUserHandle().getIdentifier()).entrySet()) { + enforcePolicyOnUserLocked(userId, entry.getValue()); + } } }); } - private <V> void enforcePolicyOnUser(int userId, PolicyState<V> policyState) { + private <V> void enforcePolicyOnUserLocked(int userId, PolicyState<V> policyState) { if (!policyState.getPolicyDefinition().isInheritable()) { return; } @@ -1239,26 +1244,28 @@ final class DevicePolicyEngine { */ @NonNull DevicePolicyState getDevicePolicyState() { - Map<UserHandle, Map<PolicyKey, android.app.admin.PolicyState<?>>> policies = - new HashMap<>(); - for (int i = 0; i < mLocalPolicies.size(); i++) { - UserHandle user = UserHandle.of(mLocalPolicies.keyAt(i)); - policies.put(user, new HashMap<>()); - for (PolicyKey policyKey : mLocalPolicies.valueAt(i).keySet()) { - policies.get(user).put( - policyKey, - mLocalPolicies.valueAt(i).get(policyKey).getParcelablePolicyState()); + synchronized (mLock) { + Map<UserHandle, Map<PolicyKey, android.app.admin.PolicyState<?>>> policies = + new HashMap<>(); + for (int i = 0; i < mLocalPolicies.size(); i++) { + UserHandle user = UserHandle.of(mLocalPolicies.keyAt(i)); + policies.put(user, new HashMap<>()); + for (PolicyKey policyKey : mLocalPolicies.valueAt(i).keySet()) { + policies.get(user).put( + policyKey, + mLocalPolicies.valueAt(i).get(policyKey).getParcelablePolicyState()); + } } - } - if (!mGlobalPolicies.isEmpty()) { - policies.put(UserHandle.ALL, new HashMap<>()); - for (PolicyKey policyKey : mGlobalPolicies.keySet()) { - policies.get(UserHandle.ALL).put( - policyKey, - mGlobalPolicies.get(policyKey).getParcelablePolicyState()); + if (!mGlobalPolicies.isEmpty()) { + policies.put(UserHandle.ALL, new HashMap<>()); + for (PolicyKey policyKey : mGlobalPolicies.keySet()) { + policies.get(UserHandle.ALL).put( + policyKey, + mGlobalPolicies.get(policyKey).getParcelablePolicyState()); + } } + return new DevicePolicyState(policies); } - return new DevicePolicyState(policies); } @@ -1266,23 +1273,25 @@ final class DevicePolicyEngine { * Removes all local and global policies set by that admin. */ void removePoliciesForAdmin(EnforcingAdmin admin) { - Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); - for (PolicyKey policy : globalPolicies) { - PolicyState<?> policyState = mGlobalPolicies.get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { - removeGlobalPolicy(policyState.getPolicyDefinition(), admin); + synchronized (mLock) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { + removeGlobalPolicy(policyState.getPolicyDefinition(), admin); + } } - } - for (int i = 0; i < mLocalPolicies.size(); i++) { - Set<PolicyKey> localPolicies = new HashSet<>( - mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get( - mLocalPolicies.keyAt(i)).get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { - removeLocalPolicy( - policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i)); + for (int i = 0; i < mLocalPolicies.size(); i++) { + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get( + mLocalPolicies.keyAt(i)).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { + removeLocalPolicy( + policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i)); + } } } } @@ -1292,23 +1301,25 @@ final class DevicePolicyEngine { * Removes all local policies for the provided {@code userId}. */ private void removeLocalPoliciesForUser(int userId) { - if (!mLocalPolicies.contains(userId)) { - // No policies on user - return; - } + synchronized (mLock) { + if (!mLocalPolicies.contains(userId)) { + // No policies on user + return; + } - Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); - Set<EnforcingAdmin> admins = new HashSet<>( - policyState.getPoliciesSetByAdmins().keySet()); - for (EnforcingAdmin admin : admins) { - removeLocalPolicy( - policyState.getPolicyDefinition(), admin, userId); + Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + Set<EnforcingAdmin> admins = new HashSet<>( + policyState.getPoliciesSetByAdmins().keySet()); + for (EnforcingAdmin admin : admins) { + removeLocalPolicy( + policyState.getPolicyDefinition(), admin, userId); + } } - } - mLocalPolicies.remove(userId); + mLocalPolicies.remove(userId); + } } /** @@ -1376,7 +1387,7 @@ final class DevicePolicyEngine { */ private void updateDeviceAdminServiceOnPolicyRemoveLocked( @NonNull EnforcingAdmin enforcingAdmin) { - if (doesAdminHavePolicies(enforcingAdmin)) { + if (doesAdminHavePoliciesLocked(enforcingAdmin)) { return; } int userId = enforcingAdmin.getUserId(); @@ -1399,7 +1410,7 @@ final class DevicePolicyEngine { /* actionForLog= */ "policy-removed"); } - private boolean doesAdminHavePolicies(@NonNull EnforcingAdmin enforcingAdmin) { + private boolean doesAdminHavePoliciesLocked(@NonNull EnforcingAdmin enforcingAdmin) { for (PolicyKey policy : mGlobalPolicies.keySet()) { PolicyState<?> policyState = mGlobalPolicies.get(policy); if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) { @@ -1420,13 +1431,17 @@ final class DevicePolicyEngine { @NonNull private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) { - return mEnforcingAdmins.contains(userId) - ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + synchronized (mLock) { + return mEnforcingAdmins.contains(userId) + ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + } } private void write() { - Log.d(TAG, "Writing device policies to file."); - new DevicePoliciesReaderWriter().writeToFileLocked(); + synchronized (mLock) { + Log.d(TAG, "Writing device policies to file."); + new DevicePoliciesReaderWriter().writeToFileLocked(); + } } // TODO(b/256852787): trigger resolving logic after loading policies as roles are recalculated @@ -1436,11 +1451,11 @@ final class DevicePolicyEngine { synchronized (mLock) { clear(); new DevicePoliciesReaderWriter().readFromFileLocked(); - reapplyAllPolicies(); + reapplyAllPoliciesLocked(); } } - private <V> void reapplyAllPolicies() { + private <V> void reapplyAllPoliciesLocked() { for (PolicyKey policy : mGlobalPolicies.keySet()) { PolicyState<?> policyState = mGlobalPolicies.get(policy); // Policy definition and value will always be of the same type @@ -1470,10 +1485,8 @@ final class DevicePolicyEngine { * <p>Note that this doesn't clear any enforcements, it only clears the data structures. */ void clearAllPolicies() { - synchronized (mLock) { - clear(); - write(); - } + clear(); + write(); } private void clear() { synchronized (mLock) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a5b15489f292..e44b8cdbaab0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2093,7 +2093,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mUserData = new SparseArray<>(); mOwners = makeOwners(injector, pathProvider); - mDevicePolicyEngine = new DevicePolicyEngine(mContext, mDeviceAdminServiceController); + mDevicePolicyEngine = new DevicePolicyEngine( + mContext, mDeviceAdminServiceController, getLockObject()); if (!mHasFeature) { // Skip the rest of the initialization @@ -7840,27 +7841,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new SecurityException("Cannot wipe data. " + restriction + " restriction is set for user " + userId); } + }); - boolean isSystemUser = userId == UserHandle.USER_SYSTEM; - boolean wipeDevice; - if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR, - adminPackage, - userId)) { - // Legacy mode - wipeDevice = isSystemUser; + boolean isSystemUser = userId == UserHandle.USER_SYSTEM; + boolean wipeDevice; + if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR, + adminPackage, + userId)) { + // Legacy mode + wipeDevice = isSystemUser; + } else { + // Explicit behaviour + if (factoryReset) { + EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( + /*admin=*/ null, + /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, + MASTER_CLEAR}, + USES_POLICY_WIPE_DATA, + adminPackage, + factoryReset ? UserHandle.USER_ALL : + getAffectedUser(calledOnParentInstance)); + wipeDevice = true; } else { - // Explicit behaviour - if (factoryReset) { - EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( - /*admin=*/ null, - /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, - MASTER_CLEAR}, - USES_POLICY_WIPE_DATA, - adminPackage, - factoryReset ? UserHandle.USER_ALL : - getAffectedUser(calledOnParentInstance)); - wipeDevice = true; - } else { + mInjector.binderWithCleanCallingIdentity(() -> { Preconditions.checkCallAuthorization(!isSystemUser, "User %s is a system user and cannot be removed", userId); boolean isLastNonHeadlessUser = getUserInfo(userId).isFull() @@ -7871,9 +7874,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Removing user %s would leave the device without any active users. " + "Consider factory resetting the device instead.", userId); - wipeDevice = false; - } + }); + wipeDevice = false; } + } + mInjector.binderWithCleanCallingIdentity(() -> { if (wipeDevice) { forceWipeDeviceNoLock( (flags & WIPE_EXTERNAL_STORAGE) != 0, diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 774f62d44045..fd478dc12c13 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -31,6 +31,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -245,10 +246,13 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + ActivityOptions activityOptions = ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED); IntentSender intentSender = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, - null, new UserHandle(mUserId)) .getIntentSender(); + | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + activityOptions.toBundle(), new UserHandle(mUserId)).getIntentSender(); Bundle result = new Bundle(); result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob); diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 Binary files differindex 138b6113ea6b..3683dca64449 100644 --- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 +++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index a614c4dd1d61..2bc66ace454b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -27,6 +27,7 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.util.DebugUtils.valueToString; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -742,24 +743,24 @@ public class ActivityManagerServiceTest { broadcastIntent(intent1, null, true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); broadcastIntent(intent2, options.toBundle(), true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), - StickyBroadcast.create(intent2, true, Process.myUid())); + StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); broadcastIntent(intent3, null, true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), - StickyBroadcast.create(intent2, true, Process.myUid())); + StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER), - StickyBroadcast.create(intent3, false, Process.myUid())); + StickyBroadcast.create(intent3, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); } @SuppressWarnings("GuardedBy") diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index e6eeaaa65e02..f4238f678f08 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -16,6 +16,8 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; @@ -246,7 +248,7 @@ public final class BroadcastQueueModernImplTest { return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo, Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM, - BackgroundStartPrivileges.NONE, false, null); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); } private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, @@ -1448,7 +1450,7 @@ public final class BroadcastQueueModernImplTest { eq(BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST), eq(BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD), anyLong(), anyLong(), anyLong(), anyInt(), nullable(String.class), - anyString(), anyInt(), anyInt()), + anyString(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()), times(1)); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 4989f841a275..0f75ea5fb7ef 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER; import static android.os.UserHandle.USER_SYSTEM; @@ -611,7 +612,7 @@ public class BroadcastQueueTest { callerApp.getPid(), callerApp.info.uid, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo, Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, - BackgroundStartPrivileges.NONE, false, null); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); } private void assertHealth() { @@ -1599,7 +1600,7 @@ public class BroadcastQueueTest { null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(), List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null, Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, - backgroundStartPrivileges, false, null); + backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN); enqueueBroadcast(r); waitForIdle(); @@ -1904,6 +1905,34 @@ public class BroadcastQueueTest { } @Test + public void testReplacePending_diffReceivers() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp); + final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp); + final BroadcastFilter receiverYellow = makeRegisteredReceiver(receiverYellowApp); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) + .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of( + withPriority(receiverGreen, 10), + withPriority(receiverBlue, 5), + withPriority(receiverYellow, 0)))); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of( + withPriority(receiverGreen, 10), + withPriority(receiverBlue, 5)))); + + waitForIdle(); + + verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane); + verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane); + verifyScheduleRegisteredReceiver(never(), receiverYellowApp, airplane); + } + + @Test public void testIdleAndBarrier() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java index 08952eab071f..f0efb795f5a1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.ACTION_LOCKED_BOOT_COMPLETED; @@ -958,7 +959,8 @@ public class BroadcastRecordTest { userId, BackgroundStartPrivileges.NONE, false /* timeoutExempt */, - filterExtrasForReceiver); + filterExtrasForReceiver, + PROCESS_STATE_UNKNOWN); } private static int getAppId(int i) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index c5ff8cc7b0d6..dd23d9f006e3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -28,6 +28,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sUptimeMillisClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -148,6 +149,9 @@ public class JobSchedulerServiceTest { // Used in JobConcurrencyManager. doReturn(mock(UserManagerInternal.class)) .when(() -> LocalServices.getService(UserManagerInternal.class)); + // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); // Called via IdleController constructor. when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); when(mContext.getResources()).thenReturn(mock(Resources.class)); @@ -168,6 +172,8 @@ public class JobSchedulerServiceTest { JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + // Make sure the uptime is at least 24 hours so that tests that rely on high uptime work. + sUptimeMillisClock = getAdvancedClock(sUptimeMillisClock, 24 * HOUR_IN_MILLIS); // Called by DeviceIdlenessTracker when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class)); @@ -313,6 +319,260 @@ public class JobSchedulerServiceTest { } @Test + public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled() { + JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // Safeguards disabled -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 UIJ timeouts. Safeguards disabled -> no penalties. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 EJ timeouts. Safeguards disabled -> no penalties. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 Reg timeouts. Safeguards disabled -> no penalties. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + } + + @Test + public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled() { + JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // No timeouts -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 UIJ timeout. No execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // Not a timeout -> 1 UIJ timeout. No execution penalty yet. + jobUij.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 UIJ timeouts. Min execution penalty only for UIJs. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 EJ timeouts. Max execution penalty for EJs. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 Reg timeouts. Max execution penalty for regular jobs. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + } + + @Test public void testGetMaxJobExecutionTimeMs() { JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs", createJobInfo(10) @@ -327,7 +587,7 @@ public class JobSchedulerServiceTest { doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) - .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); grantRunUserInitiatedJobsPermission(true); assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, @@ -337,6 +597,306 @@ public class JobSchedulerServiceTest { mService.getMaxJobExecutionTimeMs(jobUIDT)); } + @Test + public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled() { + JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + QuotaController quotaController = mService.getQuotaController(); + spyOn(quotaController); + TareController tareController = mService.getTareController(); + spyOn(tareController); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // Safeguards disabled -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 UIJ timeouts. Safeguards disabled -> no penalties. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 EJ timeouts. Safeguards disabled -> no penalties. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 Reg timeouts. Safeguards disabled -> no penalties. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + } + + @Test + public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled() { + JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + QuotaController quotaController = mService.getQuotaController(); + spyOn(quotaController); + TareController tareController = mService.getTareController(); + spyOn(tareController); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // No timeouts -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 UIJ timeout. No max execution penalty yet. + jobUij.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 UIJ timeouts. Max execution penalty only for UIJs. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 EJ timeout. No max execution penalty yet. + jobEj.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 EJ timeouts. Max execution penalty for EJs. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 reg timeout. No max execution penalty yet. + jobReg.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 Reg timeouts. Max execution penalty for regular jobs. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + } + /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} @@ -1226,6 +1786,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1249,6 +1810,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1270,6 +1832,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1292,6 +1855,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1315,6 +1879,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(false).build(); final JobWorkItem item = new JobWorkItem.Builder().build(); @@ -1337,6 +1902,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); final JobWorkItem item = new JobWorkItem.Builder().build(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 2180a781e437..2b56ea8bba33 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -73,6 +73,7 @@ import android.telephony.TelephonyManager; import android.util.DataUnit; import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.net.NetworkPolicyManagerInternal; @@ -124,6 +125,10 @@ public class ConnectivityControllerTest { LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); + // Used in JobStatus. + LocalServices.removeServiceForTest(JobSchedulerInternal.class); + LocalServices.addService(JobSchedulerInternal.class, mock(JobSchedulerInternal.class)); + // Freeze the clocks at this moment in time JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 05780ebe6c4b..1de7e3719112 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; +import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; @@ -45,6 +46,8 @@ import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.when; @@ -582,6 +585,40 @@ public class JobStatusTest { } @Test + public void testGetEffectiveStandbyBucket_buggyApp() { + when(mJobSchedulerInternal.isAppConsideredBuggy( + anyInt(), anyString(), anyInt(), anyString())) + .thenReturn(true); + + final JobInfo jobInfo = new JobInfo.Builder(1234, TEST_JOB_COMPONENT).build(); + JobStatus job = createJobStatus(jobInfo); + + // Exempt apps be exempting. + job.setStandbyBucket(EXEMPTED_INDEX); + assertEquals(EXEMPTED_INDEX, job.getEffectiveStandbyBucket()); + + // Actual bucket is higher than the buggy cap, so the cap comes into effect. + job.setStandbyBucket(ACTIVE_INDEX); + assertEquals(WORKING_INDEX, job.getEffectiveStandbyBucket()); + + // Buckets at the cap or below shouldn't be affected. + job.setStandbyBucket(WORKING_INDEX); + assertEquals(WORKING_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(FREQUENT_INDEX); + assertEquals(FREQUENT_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(RARE_INDEX); + assertEquals(RARE_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(RESTRICTED_INDEX); + assertEquals(RESTRICTED_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(NEVER_INDEX); + assertEquals(NEVER_INDEX, job.getEffectiveStandbyBucket()); + } + + @Test public void testModifyingInternalFlags() { final JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar")) diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java index fb59ea2bb63b..7cc01e1b4292 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java @@ -58,6 +58,7 @@ import android.util.SparseArray; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.controllers.PrefetchController.PcConstants; @@ -135,6 +136,9 @@ public class PrefetchControllerTest { when(mJobSchedulerService.getPackagesForUidLocked(anyInt())) .thenAnswer(invocationOnMock -> mPackagesForUid.get(invocationOnMock.getArgument(0))); + // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions // in the past, and PrefetchController sometimes floors values at 0, so if the test time diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 6f713e0fad64..dce162c58d0b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -85,6 +85,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; @@ -190,6 +191,8 @@ public class QuotaControllerTest { doReturn(mPowerAllowlistInternal) .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Used in QuotaController.Handler. diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index 51e521d8ffe4..206af5b6aea6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -17,8 +17,10 @@ package com.android.server.wallpaper; import static android.app.WallpaperManager.COMMAND_REAPPLY; +import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.os.FileObserver.CLOSE_WRITE; +import static android.os.UserHandle.MIN_SECONDARY_USER_ID; import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.DEFAULT_DISPLAY; @@ -106,6 +108,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.List; /** * Tests for the {@link WallpaperManagerService} class. @@ -172,12 +175,12 @@ public class WallpaperManagerServiceTests { sImageWallpaperComponentName = ComponentName.unflattenFromString( sContext.getResources().getString(R.string.image_wallpaper_component)); // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper. - sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext); + sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext); if (sDefaultWallpaperComponent == null) { sDefaultWallpaperComponent = sImageWallpaperComponentName; doReturn(sImageWallpaperComponentName).when(() -> - WallpaperManager.getCmfDefaultWallpaperComponent(any())); + WallpaperManager.getDefaultWallpaperComponent(any())); } else { sContext.addMockService(sDefaultWallpaperComponent, sWallpaperService); } @@ -262,6 +265,25 @@ public class WallpaperManagerServiceTests { } /** + * Tests that the fundamental fields are set by the main WallpaperData constructor + */ + @Test + public void testWallpaperDataConstructor() { + final int testUserId = MIN_SECONDARY_USER_ID; + for (int which: List.of(FLAG_LOCK, FLAG_SYSTEM)) { + WallpaperData newWallpaperData = new WallpaperData(testUserId, which); + assertEquals(which, newWallpaperData.mWhich); + assertEquals(testUserId, newWallpaperData.userId); + + WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which); + assertEquals(wallpaperData.cropFile.getAbsolutePath(), + newWallpaperData.cropFile.getAbsolutePath()); + assertEquals(wallpaperData.wallpaperFile.getAbsolutePath(), + newWallpaperData.wallpaperFile.getAbsolutePath()); + } + } + + /** * Tests that internal basic data should be correct after boot up. */ @Test @@ -405,10 +427,7 @@ public class WallpaperManagerServiceTests { fail("exception occurred while writing system wallpaper attributes"); } - WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId, - systemWallpaperData.wallpaperFile.getParentFile(), - systemWallpaperData.wallpaperFile.getAbsolutePath(), - systemWallpaperData.cropFile.getAbsolutePath()); + WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM); try { TypedXmlPullParser parser = Xml.newBinaryPullParser(); mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true); diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index f44c1d18614d..4315254f68a9 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" /> @@ -36,6 +37,7 @@ <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" /> <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9166b3d75f60..cebc5409c795 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -61,6 +61,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; +import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; @@ -80,6 +81,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; @@ -119,12 +121,14 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import android.Manifest; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.ActivityManager; @@ -181,11 +185,14 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.Parcel; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.WorkSource; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.MediaStore; @@ -351,6 +358,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private PermissionManager mPermissionManager; @Mock private DevicePolicyManagerInternal mDevicePolicyManager; + @Mock + private PowerManager mPowerManager; + private final ArrayList<WakeLock> mAcquiredWakeLocks = new ArrayList<>(); private final TestPostNotificationTrackerFactory mPostNotificationTrackerFactory = new TestPostNotificationTrackerFactory(); @@ -431,8 +441,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final List<PostNotificationTracker> mCreatedTrackers = new ArrayList<>(); @Override - public PostNotificationTracker newTracker() { - PostNotificationTracker tracker = PostNotificationTrackerFactory.super.newTracker(); + public PostNotificationTracker newTracker(@Nullable WakeLock optionalWakeLock) { + PostNotificationTracker tracker = PostNotificationTrackerFactory.super.newTracker( + optionalWakeLock); mCreatedTrackers.add(tracker); return tracker; } @@ -563,6 +574,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); + // Use the real PowerManager to back up the mock w.r.t. creating WakeLocks. + // This is because 1) we need a mock to verify() calls and tracking the created WakeLocks, + // but 2) PowerManager and WakeLock perform their own checks (e.g. correct arguments, don't + // call release twice, etc) and we want the test to fail if such misuse happens, too. + PowerManager realPowerManager = mContext.getSystemService(PowerManager.class); + when(mPowerManager.newWakeLock(anyInt(), anyString())).then( + (Answer<WakeLock>) invocation -> { + WakeLock wl = realPowerManager.newWakeLock(invocation.getArgument(0), + invocation.getArgument(1)); + mAcquiredWakeLocks.add(wl); + return wl; + }); + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); + // apps allowed as convos mService.setStringArrayResourceValue(PKG_O); @@ -579,7 +606,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager, - mPostNotificationTrackerFactory); + mPowerManager, mPostNotificationTrackerFactory); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -686,6 +713,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @After + public void assertAllWakeLocksReleased() { + for (WakeLock wakeLock : mAcquiredWakeLocks) { + assertThat(wakeLock.isHeld()).isFalse(); + } + } + + @After public void tearDown() throws Exception { if (mFile != null) mFile.delete(); clearDeviceConfig(); @@ -1486,7 +1520,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -1507,7 +1541,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -1803,6 +1837,128 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void enqueueNotification_acquiresAndReleasesWakeLock() throws Exception { + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_acquiresAndReleasesWakeLock", 0, + generateNotificationRecord(null).getNotification(), 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_throws_acquiresAndReleasesWakeLock() throws Exception { + // Simulate not enqueued due to rejected inputs. + assertThrows(Exception.class, + () -> mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_throws_acquiresAndReleasesWakeLock", 0, + /* notification= */ null, 0)); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_notEnqueued_acquiresAndReleasesWakeLock() throws Exception { + // Simulate not enqueued due to snoozing inputs. + when(mSnoozeHelper.getSnoozeContextForUnpostedNotification(anyInt(), any(), any())) + .thenReturn("zzzzzzz"); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_notEnqueued_acquiresAndReleasesWakeLock", 0, + generateNotificationRecord(null).getNotification(), 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_notPosted_acquiresAndReleasesWakeLock() throws Exception { + // Simulate enqueued but not posted due to missing small icon. + Notification notif = new Notification.Builder(mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .build(); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_notPosted_acquiresAndReleasesWakeLock", 0, + notif, 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + // NLSes were not called. + verify(mListeners, never()).prepareNotifyPostedLocked(any(), any(), anyBoolean()); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_setsWakeLockWorkSource() throws Exception { + // Use a "full" mock for the PowerManager (instead of the one that delegates to the real + // service) so we can return a mocked WakeLock that we can verify() on. + reset(mPowerManager); + WakeLock wakeLock = mock(WakeLock.class); + when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(wakeLock); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + InOrder inOrder = inOrder(mPowerManager, wakeLock); + inOrder.verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + inOrder.verify(wakeLock).setWorkSource(eq(new WorkSource(mUid, PKG))); + inOrder.verify(wakeLock).acquire(anyLong()); + inOrder.verify(wakeLock).release(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void enqueueNotification_wakeLockSystemPropertyOff_noWakeLock() throws Exception { + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + verifyZeroInteractions(mPowerManager); + } + + @Test + public void enqueueNotification_wakeLockDeviceConfigOff_noWakeLock() throws Exception { + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "false", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + verifyZeroInteractions(mPowerManager); + } + + @Test public void testCancelNonexistentNotification() throws Exception { mBinderService.cancelNotificationWithTag(PKG, PKG, "testCancelNonexistentNotification", 0, 0); @@ -4361,7 +4517,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4380,7 +4536,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4400,7 +4556,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4420,7 +4576,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), - update.getUid(), mPostNotificationTrackerFactory.newTracker()); + update.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4434,13 +4590,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); r = generateNotificationRecord(mTestNotificationChannel, 1, null, false); r.setCriticality(CriticalNotificationExtractor.CRITICAL); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); mService.addEnqueuedNotification(r); runnable.run(); @@ -5090,7 +5246,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.new PostNotificationRunnable(original.getKey(), original.getSbn().getPackageName(), original.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -5114,7 +5270,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -7536,7 +7692,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10234,7 +10390,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10251,7 +10407,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10268,7 +10424,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10361,7 +10517,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // normal blocked notifications - blocked mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10379,7 +10535,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10392,7 +10548,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats, never()).registerBlocked(any()); @@ -10406,7 +10562,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats, never()).registerBlocked(any()); @@ -10420,7 +10576,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10435,7 +10591,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10448,7 +10604,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 66c1e35754c5..81c573d8fb1e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -48,6 +48,7 @@ import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.Looper; +import android.os.PowerManager; import android.os.UserHandle; import android.os.UserManager; import android.permission.PermissionManager; @@ -169,6 +170,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(UsageStatsManagerInternal.class), mock(TelecomManager.class), mock(NotificationChannelLogger.class), new TestableFlagResolver(), mock(PermissionManager.class), + mock(PowerManager.class), new NotificationManagerService.PostNotificationTrackerFactory() {}); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 0044e2e54b5a..4290f4b9ee80 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -1059,4 +1060,18 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertEquals(0, mAtm.getActivityInterceptorCallbacks().size()); mAtm.mInternal.unregisterActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID); } + + @Test + public void testFocusTopTask() { + final ActivityRecord homeActivity = new ActivityBuilder(mAtm) + .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()) + .build(); + final Task pinnedTask = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_PINNED) + .build(); + mAtm.focusTopTask(mDisplayContent.mDisplayId); + + assertTrue(homeActivity.getTask().isFocused()); + assertFalse(pinnedTask.isFocused()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 3ca35ef7cc26..8e015d4d228d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -40,7 +40,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -51,6 +53,8 @@ import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; import android.content.pm.ActivityInfo.ScreenOrientation; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Configuration.Orientation; import android.hardware.camera2.CameraManager; @@ -84,7 +88,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { private static final String TEST_PACKAGE_2 = "com.test.package.two"; private static final String CAMERA_ID_1 = "camera-1"; private static final String CAMERA_ID_2 = "camera-2"; - + private static final String TEST_PACKAGE_1_LABEL = "testPackage1"; private CameraManager mMockCameraManager; private Handler mMockHandler; private LetterboxConfiguration mLetterboxConfiguration; @@ -133,17 +137,27 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { } @Test - public void testOpenedCameraInSplitScreen_showToast() { + public void testOpenedCameraInSplitScreen_showToast() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); spyOn(mTask); spyOn(mDisplayRotationCompatPolicy); doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode(); + final PackageManager mockPackageManager = mock(PackageManager.class); + final ApplicationInfo mockApplicationInfo = mock(ApplicationInfo.class); + when(mContext.getPackageManager()).thenReturn(mockPackageManager); + when(mockPackageManager.getApplicationInfo(anyString(), anyInt())) + .thenReturn(mockApplicationInfo); + + doReturn(TEST_PACKAGE_1_LABEL).when(mockPackageManager) + .getApplicationLabel(mockApplicationInfo); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); verify(mDisplayRotationCompatPolicy).showToast( - R.string.display_rotation_camera_compat_toast_in_split_screen); + R.string.display_rotation_camera_compat_toast_in_multi_window, + TEST_PACKAGE_1_LABEL); } @Test @@ -157,7 +171,8 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); verify(mDisplayRotationCompatPolicy, never()).showToast( - R.string.display_rotation_camera_compat_toast_in_split_screen); + R.string.display_rotation_camera_compat_toast_in_multi_window, + TEST_PACKAGE_1_LABEL); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index fb29d3adb52b..abf21a57dd40 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -641,6 +641,31 @@ public class RootTaskTests extends WindowTestsBase { // Home split secondary and home task should be invisible. assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */)); + + // Put another task on top of primary split. + final Task topSplitPrimary = new TaskBuilder(mSupervisor).setParentTask(organizer.mPrimary) + .setCreateActivity(true).build(); + doReturn(false).when(topSplitPrimary).isTranslucent(any()); + // Convert the fullscreen translucent task to opaque. + doReturn(false).when(translucentRootTask).isTranslucent(any()); + translucentRootTask.moveToFront("test"); + // The tasks of primary split are occluded by the fullscreen opaque task. + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + organizer.mPrimary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + topSplitPrimary.getVisibility(null /* starting */)); + // Make primary split root transient-hide. + spyOn(splitPrimary.mTransitionController); + doReturn(true).when(splitPrimary.mTransitionController).isTransientHide( + organizer.mPrimary); + // The split root and its top become visible. + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + organizer.mPrimary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + topSplitPrimary.getVisibility(null /* starting */)); + // The bottom of primary split becomes invisible because it is occluded by topSplitPrimary. + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + splitPrimary.getVisibility(null /* starting */)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index e74ad3364b63..082122e33175 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -406,7 +406,6 @@ public class SizeCompatTests extends WindowTestsBase { clearInvocations(translucentActivity.mLetterboxUiController); // We destroy the first opaque activity - mActivity.setState(DESTROYED, "testing"); mActivity.removeImmediately(); // Check that updateInheritedLetterbox() is invoked again diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index b59f027fec83..11267268a6fa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1346,6 +1346,16 @@ public class TransitionTests extends WindowTestsBase { activity1.setVisible(false); abortTransition.abort(); assertTrue(activity1.isVisible()); + + // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared. + final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN); + noChangeTransition.collect(activity1); + activity1.setVisibleRequested(false); + activity1.setState(ActivityRecord.State.INITIALIZING, "test"); + activity1.mLaunchTaskBehind = true; + mWm.mSyncEngine.abort(noChangeTransition.getSyncId()); + noChangeTransition.finishTransition(); + assertTrue(activity1.mLaunchTaskBehind); } @Test diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 188894339f6a..5b1c6b1c88b8 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -10318,7 +10318,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP); sDefaults.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, 300); - sDefaults.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, 0); + sDefaults.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, -1); // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml index bedf0990a188..bfebb42ad244 100644 --- a/tests/InputMethodStressTest/AndroidTest.xml +++ b/tests/InputMethodStressTest/AndroidTest.xml @@ -19,6 +19,7 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" /> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> </target_preparer> diff --git a/tests/SilkFX/res/layout/gainmap_transform_test.xml b/tests/SilkFX/res/layout/gainmap_transform_test.xml new file mode 100644 index 000000000000..5aeb53661cbc --- /dev/null +++ b/tests/SilkFX/res/layout/gainmap_transform_test.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.test.silkfx.hdr.GainmapTransformsTest xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/original" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Original" /> + + <Button + android:id="@+id/scaled" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Scaled (1/3)" /> + + <Button + android:id="@+id/rotate_90" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Rotate 90" /> + + <Button + android:id="@+id/rotate_90_scaled" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Rot90+Scale" /> + + <Button + android:id="@+id/crop" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Crop" /> + + <Button + android:id="@+id/crop_200" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Crop 200" /> + + </LinearLayout> + + <TextView + android:id="@+id/source_info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1"> + + <TextView + android:id="@+id/sdr_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageView + android:id="@+id/sdr_source" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="8dp" + android:scaleType="fitStart" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1"> + + <TextView + android:id="@+id/gainmap_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageView + android:id="@+id/gainmap" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="8dp" + android:scaleType="fitStart" /> + </LinearLayout> + + </LinearLayout> + +</com.android.test.silkfx.hdr.GainmapTransformsTest>
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt index a6cdbb9865bc..59a6078376cf 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt @@ -55,7 +55,9 @@ private val AllDemos = listOf( Demo("Color Grid", R.layout.color_grid), Demo("Gradient Sweep", R.layout.gradient_sweep), Demo("Gainmap Image", R.layout.gainmap_image), - Demo("Gainmap Decode Test", R.layout.gainmap_decode_test, commonControls = false) + Demo("Gainmap Decode Test", R.layout.gainmap_decode_test, commonControls = false), + Demo("Gainmap Transform Test", R.layout.gainmap_transform_test, + commonControls = false) )), DemoGroup("Materials", listOf( Demo("Glass", GlassActivity::class), diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt index a004fb5a4305..585320aee615 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt @@ -17,7 +17,12 @@ package com.android.test.silkfx.hdr import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.ColorMatrixColorFilter +import android.graphics.Gainmap import android.graphics.ImageDecoder +import android.graphics.Paint import android.graphics.Rect import android.util.AttributeSet import android.widget.Button @@ -34,6 +39,25 @@ enum class DecodeMode { CropedSquaredScaled33 } +fun gainmapVisualizer(gainmap: Gainmap): Bitmap { + val map = gainmap.gainmapContents + val gainmapVisualizer = Bitmap.createBitmap(map.width, map.height, + Bitmap.Config.ARGB_8888) + val canvas = Canvas(gainmapVisualizer!!) + val paint = Paint() + paint.colorFilter = ColorMatrixColorFilter( + floatArrayOf( + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 0f, 255f + ) + ) + canvas.drawBitmap(map, 0f, 0f, paint) + canvas.setBitmap(null) + return gainmapVisualizer +} + class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { private fun decode(mode: DecodeMode) { @@ -71,7 +95,7 @@ class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(c } } - val gainmapContents = gainmapImage.gainmap!!.gainmapContents!! + val gainmapContents = gainmapImage.gainmap?.let { gainmapVisualizer(it) } val sdrBitmap = gainmapImage.also { it.gainmap = null } findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap) @@ -80,7 +104,7 @@ class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(c findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents) findViewById<TextView>(R.id.gainmap_label)!!.text = - "Gainmap Size: ${gainmapContents.width}x${gainmapContents.height}" + "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}" } override fun onFinishInflate() { diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt new file mode 100644 index 000000000000..20984fae2133 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt @@ -0,0 +1,116 @@ +/* + * 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.test.silkfx.hdr + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.ImageDecoder +import android.graphics.Matrix +import android.util.AttributeSet +import android.widget.Button +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.android.test.silkfx.R + +class GainmapTransformsTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { + + private val sourceImage = loadSample() + + private fun loadSample(): Bitmap { + val source = ImageDecoder.createSource(resources.assets, + "gainmaps/${context.assets.list("gainmaps")!![0]}") + + return ImageDecoder.decodeBitmap(source) { decoder, info, source -> + decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE + } + } + + private fun process(transform: (Bitmap) -> Bitmap) { + val result = transform(sourceImage) + + val gainmapContents = result.gainmap?.let { gainmapVisualizer(it) } + val sdrBitmap = result.also { it.gainmap = null } + + findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap) + findViewById<TextView>(R.id.sdr_label)!!.text = + "SDR Size: ${sdrBitmap.width}x${sdrBitmap.height}" + + findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents) + findViewById<TextView>(R.id.gainmap_label)!!.text = + "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}" + } + + override fun onFinishInflate() { + super.onFinishInflate() + val sourceInfo = findViewById<TextView>(R.id.source_info)!! + sourceInfo.text = "Original size ${sourceImage.width}x${sourceImage.height}" + process { it.copy(Bitmap.Config.ARGB_8888, false) } + + findViewById<Button>(R.id.original)!!.setOnClickListener { + process { it.copy(Bitmap.Config.ARGB_8888, false) } + } + + findViewById<Button>(R.id.scaled)!!.setOnClickListener { + process { Bitmap.createScaledBitmap(it, it.width / 3, it.height / 3, true) } + } + + findViewById<Button>(R.id.rotate_90)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat()) + Bitmap.createBitmap(it, 0, 0, width, height, m, false) + } + } + + findViewById<Button>(R.id.rotate_90_scaled)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat()) + m.preScale(.3f, .3f) + Bitmap.createBitmap(it, 0, 0, width, height, m, false) + } + } + + findViewById<Button>(R.id.crop)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + Bitmap.createBitmap(it, width / 2, height / 2, + width / 4, height / 4, null, false) + } + } + + findViewById<Button>(R.id.crop_200)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(200.0f, (width / 2).toFloat(), (height / 2).toFloat()) + Bitmap.createBitmap(it, width / 2, height / 2, + width / 4, height / 4, m, false) + } + } + } +}
\ No newline at end of file |