diff options
1026 files changed, 19698 insertions, 9921 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 904109b569b8..22f736e367c3 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -27,6 +27,7 @@ aconfig_srcjars = [ ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}", ":android.security.flags-aconfig-java{.generated_srcjars}", ":android.service.chooser.flags-aconfig-java{.generated_srcjars}", + ":android.service.dreams.flags-aconfig-java{.generated_srcjars}", ":android.service.notification.flags-aconfig-java{.generated_srcjars}", ":android.view.flags-aconfig-java{.generated_srcjars}", ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}", @@ -231,7 +232,6 @@ java_aconfig_library { name: "android.security.flags-aconfig-java-host", aconfig_declarations: "android.security.flags-aconfig", host_supported: true, - mode: "test", defaults: ["framework-minus-apex-aconfig-java-defaults"], } @@ -742,6 +742,19 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// Dreams +aconfig_declarations { + name: "android.service.dreams.flags-aconfig", + package: "android.service.dreams", + srcs: ["core/java/android/service/dreams/flags.aconfig"], +} + +java_aconfig_library { + name: "android.service.dreams.flags-aconfig-java", + aconfig_declarations: "android.service.dreams.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Notifications aconfig_declarations { name: "android.service.notification.flags-aconfig", diff --git a/Android.bp b/Android.bp index fa7c97d3d21a..676a0f51d3f6 100644 --- a/Android.bp +++ b/Android.bp @@ -106,7 +106,7 @@ filegroup { ":android.hardware.radio.voice-V3-java-source", ":android.hardware.security.keymint-V3-java-source", ":android.hardware.security.secureclock-V1-java-source", - ":android.hardware.thermal-V1-java-source", + ":android.hardware.thermal-V2-java-source", ":android.hardware.tv.tuner-V2-java-source", ":android.security.apc-java-source", ":android.security.authorization-java-source", @@ -37,3 +37,5 @@ per-file SQLITE_OWNERS = file:/SQLITE_OWNERS per-file *ravenwood* = file:ravenwood/OWNERS per-file *Ravenwood* = file:ravenwood/OWNERS + +per-file PERFORMANCE_OWNERS = file:/PERFORMANCE_OWNERS diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS index 9452ea35f3e4..48a020130445 100644 --- a/PERFORMANCE_OWNERS +++ b/PERFORMANCE_OWNERS @@ -3,3 +3,6 @@ edgararriaga@google.com dualli@google.com carmenjackson@google.com philipcuadra@google.com +shayba@google.com +jdduke@google.com +shombert@google.com 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 4aadc903ba23..03891bbec56a 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 @@ -98,7 +98,10 @@ public final class BackgroundJobsController extends StateController { switch (action) { case Intent.ACTION_PACKAGE_RESTARTED: { synchronized (mLock) { - mPackageStoppedState.add(pkgUid, pkgName, Boolean.TRUE); + // ACTION_PACKAGE_RESTARTED doesn't always mean the app is placed and kept + // in the stopped state, so don't put TRUE in the cache. Remove any existing + // entry and rely on an explicit call to PackageManager's isStopped() API. + mPackageStoppedState.delete(pkgUid, pkgName); updateJobRestrictionsForUidLocked(pkgUid, false); } } @@ -126,6 +129,7 @@ public final class BackgroundJobsController extends StateController { } @Override + @GuardedBy("mLock") public void startTrackingLocked() { mAppStateTracker.addListener(mForceAppStandbyListener); final IntentFilter filter = new IntentFilter(); @@ -137,15 +141,18 @@ public final class BackgroundJobsController extends StateController { } @Override + @GuardedBy("mLock") public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN); } @Override + @GuardedBy("mLock") public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) { } @Override + @GuardedBy("mLock") public void evaluateStateLocked(JobStatus jobStatus) { if (jobStatus.isRequestedExpeditedJob()) { // Only requested-EJs could have their run-in-bg constraint change outside of something @@ -155,11 +162,13 @@ public final class BackgroundJobsController extends StateController { } @Override + @GuardedBy("mLock") public void onAppRemovedLocked(String packageName, int uid) { mPackageStoppedState.delete(uid, packageName); } @Override + @GuardedBy("mLock") public void onUserRemovedLocked(int userId) { for (int u = mPackageStoppedState.numMaps() - 1; u >= 0; --u) { final int uid = mPackageStoppedState.keyAt(u); @@ -170,6 +179,7 @@ public final class BackgroundJobsController extends StateController { } @Override + @GuardedBy("mLock") public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { pw.println("Aconfig flags:"); @@ -223,6 +233,7 @@ public final class BackgroundJobsController extends StateController { } @Override + @GuardedBy("mLock") public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) { final long token = proto.start(fieldId); @@ -260,14 +271,17 @@ public final class BackgroundJobsController extends StateController { proto.end(token); } + @GuardedBy("mLock") private void updateAllJobRestrictionsLocked() { updateJobRestrictionsLocked(/*filterUid=*/ -1, UNKNOWN); } + @GuardedBy("mLock") private void updateJobRestrictionsForUidLocked(int uid, boolean isActive) { updateJobRestrictionsLocked(uid, (isActive) ? KNOWN_ACTIVE : KNOWN_INACTIVE); } + @GuardedBy("mLock") private void updateJobRestrictionsLocked(int filterUid, int newActiveState) { mUpdateJobFunctor.prepare(newActiveState); @@ -295,28 +309,36 @@ public final class BackgroundJobsController extends StateController { } } - private boolean isPackageStopped(String packageName, int uid) { + @GuardedBy("mLock") + private boolean isPackageStoppedLocked(String packageName, int uid) { if (mPackageStoppedState.contains(uid, packageName)) { return mPackageStoppedState.get(uid, packageName); } - final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); - mPackageStoppedState.add(uid, packageName, isStopped); - return isStopped; + + try { + final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); + mPackageStoppedState.add(uid, packageName, isStopped); + return isStopped; + } catch (IllegalArgumentException e) { + Slog.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + return false; + } } + @GuardedBy("mLock") boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed, int activeState) { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean isSourcePkgStopped = - isPackageStopped(jobStatus.getSourcePackageName(), jobStatus.getSourceUid()); + isPackageStoppedLocked(jobStatus.getSourcePackageName(), jobStatus.getSourceUid()); final boolean isCallingPkgStopped; if (!jobStatus.isProxyJob()) { isCallingPkgStopped = isSourcePkgStopped; } else { isCallingPkgStopped = - isPackageStopped(jobStatus.getCallingPackageName(), jobStatus.getUid()); + isPackageStoppedLocked(jobStatus.getCallingPackageName(), jobStatus.getUid()); } final boolean isStopped = android.content.pm.Flags.stayStopped() && (isCallingPkgStopped || isSourcePkgStopped); 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 e06006f25d3f..f40508302ee3 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 @@ -1769,7 +1769,8 @@ public final class ConnectivityController extends RestrictingController implemen @VisibleForTesting class CcConfig { - private boolean mFlexIsEnabled = FlexibilityController.FcConfig.DEFAULT_FLEXIBILITY_ENABLED; + private boolean mFlexIsEnabled = + FlexibilityController.FcConfig.DEFAULT_APPLIED_CONSTRAINTS != 0; private boolean mShouldReprocessNetworkCapabilities = false; /** diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java index 13a474ccf451..0e67b9ac944f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java @@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY; -import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; import android.annotation.ElapsedRealtimeLong; @@ -74,17 +73,10 @@ public final class FlexibilityController extends StateController { private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY; /** List of all flexible constraints. */ - private static final int FLEXIBLE_CONSTRAINTS = + @VisibleForTesting + static final int FLEXIBLE_CONSTRAINTS = JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; - private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = - Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS); - - static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS = - Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS); - - static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS); - private static final long NO_LIFECYCLE_END = Long.MAX_VALUE; /** @@ -100,9 +92,15 @@ public final class FlexibilityController extends StateController { private long mUnseenConstraintGracePeriodMs = FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS; - @VisibleForTesting + /** Set of constraints supported on this device for flex scheduling. */ + private final int mSupportedFlexConstraints; + @GuardedBy("mLock") - boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED; + private boolean mFlexibilityEnabled; + + /** Set of constraints that will be used in the flex policy. */ + @GuardedBy("mLock") + private int mAppliedConstraints = FcConfig.DEFAULT_APPLIED_CONSTRAINTS; private long mMinTimeBetweenFlexibilityAlarmsMs = FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS; @@ -118,9 +116,6 @@ public final class FlexibilityController extends StateController { */ private int[] mPercentToDropConstraints; - @VisibleForTesting - boolean mDeviceSupportsFlexConstraints; - /** * Keeps track of what flexible constraints are satisfied at the moment. * Is updated by the other controllers. @@ -178,7 +173,7 @@ public final class FlexibilityController extends StateController { if (!js.hasFlexibilityConstraint()) { continue; } - mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed); } } @@ -186,15 +181,23 @@ public final class FlexibilityController extends StateController { }; private static final int MSG_UPDATE_JOBS = 0; + private static final int MSG_UPDATE_JOB = 1; public FlexibilityController( JobSchedulerService service, PrefetchController prefetchController) { super(service); mHandler = new FcHandler(AppSchedulingModuleThread.get().getLooper()); - mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE); - mFlexibilityEnabled &= mDeviceSupportsFlexConstraints; - mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS); + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) { + // Embedded devices have no user-installable apps. Assume all jobs are critical + // and can't be flexed. + mSupportedFlexConstraints = 0; + } else { + // TODO(236261941): handle devices without a battery + mSupportedFlexConstraints = FLEXIBLE_CONSTRAINTS; + } + mFlexibilityEnabled = (mAppliedConstraints & mSupportedFlexConstraints) != 0; + mFlexibilityTracker = new FlexibilityTracker(Integer.bitCount(mSupportedFlexConstraints)); mFcConfig = new FcConfig(); mFlexibilityAlarmQueue = new FlexibilityAlarmQueue( mContext, AppSchedulingModuleThread.get().getLooper()); @@ -218,10 +221,12 @@ public final class FlexibilityController extends StateController { public void maybeStartTrackingJobLocked(JobStatus js, JobStatus lastJob) { if (js.hasFlexibilityConstraint()) { final long nowElapsed = sElapsedRealtimeClock.millis(); - if (!mDeviceSupportsFlexConstraints) { + if (mSupportedFlexConstraints == 0) { js.setFlexibilityConstraintSatisfied(nowElapsed, true); return; } + js.setNumAppliedFlexibleConstraints( + Integer.bitCount(getRelevantAppliedConstraintsLocked(js))); js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js)); mFlexibilityTracker.add(js); js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY); @@ -266,6 +271,14 @@ public final class FlexibilityController extends StateController { || mService.isCurrentlyRunningLocked(js); } + @VisibleForTesting + @GuardedBy("mLock") + int getRelevantAppliedConstraintsLocked(@NonNull JobStatus js) { + final int relevantConstraints = SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0); + return mAppliedConstraints & relevantConstraints; + } + /** * Returns whether there are enough constraints satisfied to allow running the job from flex's * perspective. This takes into account unseen constraint combinations and expectations around @@ -274,7 +287,7 @@ public final class FlexibilityController extends StateController { @VisibleForTesting @GuardedBy("mLock") boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) { - final int satisfiedConstraints = mSatisfiedFlexibleConstraints + final int satisfiedConstraints = mSatisfiedFlexibleConstraints & mAppliedConstraints & (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS | (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0)); final int numSatisfied = Integer.bitCount(satisfiedConstraints); @@ -296,8 +309,7 @@ public final class FlexibilityController extends StateController { // count have not been seen recently enough, then assume they won't be seen anytime soon, // so don't force the job to wait longer. If any combinations with a higher count have been // seen recently, then the job can potentially wait for those combinations. - final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS - | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0)); + final int irrelevantConstraints = ~getRelevantAppliedConstraintsLocked(js); for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) { final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i); if ((constraints & irrelevantConstraints) != 0) { @@ -515,9 +527,9 @@ public final class FlexibilityController extends StateController { for (int j = 0; j < mFlexibilityTracker.size(); j++) { final ArraySet<JobStatus> jobs = mFlexibilityTracker .getJobsByNumRequiredConstraints(j); - for (int i = 0; i < jobs.size(); i++) { + for (int i = jobs.size() - 1; i >= 0; --i) { JobStatus js = jobs.valueAt(i); - mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityTracker.updateFlexibleConstraints(js, nowElapsed); mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed); if (js.setFlexibilityConstraintSatisfied( nowElapsed, isFlexibilitySatisfiedLocked(js))) { @@ -579,18 +591,46 @@ public final class FlexibilityController extends StateController { mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).remove(js); } - public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) { + /** + * Updates applied and dropped constraints for the job. + */ + public void updateFlexibleConstraints(JobStatus js, long nowElapsed) { + final int prevNumRequired = js.getNumRequiredFlexibleConstraints(); + + final int numAppliedConstraints = + Integer.bitCount(getRelevantAppliedConstraintsLocked(js)); + js.setNumAppliedFlexibleConstraints(numAppliedConstraints); + final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed); int toDrop = 0; - final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS - + (js.canApplyTransportAffinities() ? 1 : 0); + for (int i = 0; i < numAppliedConstraints; i++) { + if (curPercent >= mPercentToDropConstraints[i]) { + toDrop++; + } + } + js.setNumDroppedFlexibleConstraints(toDrop); + + if (prevNumRequired == js.getNumRequiredFlexibleConstraints()) { + return; + } + mTrackedJobs.get(prevNumRequired).remove(js); + add(js); + } + + /** + * Calculates the number of constraints that should be dropped for the job, based on how + * far along the job is into its lifecycle. + */ + public void calculateNumDroppedConstraints(JobStatus js, long nowElapsed) { + final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed); + int toDrop = 0; + final int jsMaxFlexibleConstraints = js.getNumAppliedFlexibleConstraints(); for (int i = 0; i < jsMaxFlexibleConstraints; i++) { if (curPercent >= mPercentToDropConstraints[i]) { toDrop++; } } - adjustJobsRequiredConstraints( - js, js.getNumDroppedFlexibleConstraints() - toDrop, nowElapsed); + setNumDroppedFlexibleConstraints(js, toDrop); } /** Returns all tracked jobs. */ @@ -599,17 +639,14 @@ public final class FlexibilityController extends StateController { } /** - * Adjusts number of required flexible constraints and sorts it into the tracker. - * Returns false if the job status's number of flexible constraints is now 0. + * Updates the number of dropped flexible constraints and sorts it into the tracker. */ - public boolean adjustJobsRequiredConstraints(JobStatus js, int adjustBy, long nowElapsed) { - if (adjustBy != 0) { + public void setNumDroppedFlexibleConstraints(JobStatus js, int numDropped) { + if (numDropped != js.getNumDroppedFlexibleConstraints()) { remove(js); - js.adjustNumRequiredFlexibleConstraints(adjustBy); - js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js)); + js.setNumDroppedFlexibleConstraints(numDropped); add(js); } - return js.getNumRequiredFlexibleConstraints() > 0; } public int size() { @@ -658,8 +695,10 @@ public final class FlexibilityController extends StateController { if (DEBUG) { Slog.d(TAG, "scheduleDropNumConstraintsAlarm: " + js.getSourcePackageName() + " " + js.getSourceUserId() + + " numApplied: " + js.getNumAppliedFlexibleConstraints() + " numRequired: " + js.getNumRequiredFlexibleConstraints() - + " numSatisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints) + + " numSatisfied: " + Integer.bitCount( + mSatisfiedFlexibleConstraints & getRelevantAppliedConstraintsLocked(js)) + " curTime: " + nowElapsed + " earliest: " + earliest + " latest: " + latest @@ -669,8 +708,9 @@ public final class FlexibilityController extends StateController { if (DEBUG) { Slog.d(TAG, "deadline proximity met: " + js); } - mFlexibilityTracker.adjustJobsRequiredConstraints(js, - -js.getNumRequiredFlexibleConstraints(), nowElapsed); + mFlexibilityTracker.setNumDroppedFlexibleConstraints(js, + js.getNumAppliedFlexibleConstraints()); + mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget(); return; } if (nextTimeElapsed == NO_LIFECYCLE_END) { @@ -696,12 +736,15 @@ public final class FlexibilityController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = 0; i < expired.size(); i++) { JobStatus js = expired.valueAt(i); - boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE); - - if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) { + if (DEBUG) { + Slog.d(TAG, "Alarm fired for " + js.toShortString()); + } + mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); + if (js.getNumRequiredFlexibleConstraints() > 0) { scheduleDropNumConstraintsAlarm(js, nowElapsed); } - if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) { + if (js.setFlexibilityConstraintSatisfied(nowElapsed, + isFlexibilitySatisfiedLocked(js))) { changedJobs.add(js); } } @@ -725,7 +768,9 @@ public final class FlexibilityController extends StateController { final long nowElapsed = sElapsedRealtimeClock.millis(); final ArraySet<JobStatus> changedJobs = new ArraySet<>(); - for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) { + final int numAppliedSystemWideConstraints = Integer.bitCount( + mAppliedConstraints & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS); + for (int o = 0; o <= numAppliedSystemWideConstraints; ++o) { final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker .getJobsByNumRequiredConstraints(o); @@ -744,6 +789,23 @@ public final class FlexibilityController extends StateController { } } break; + + case MSG_UPDATE_JOB: + synchronized (mLock) { + final JobStatus js = (JobStatus) msg.obj; + if (DEBUG) { + Slog.d("blah", "Checking on " + js.toShortString()); + } + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (js.setFlexibilityConstraintSatisfied( + nowElapsed, isFlexibilitySatisfiedLocked(js))) { + // TODO(141645789): add method that will take a single job + ArraySet<JobStatus> changedJob = new ArraySet<>(); + changedJob.add(js); + mStateChangedListener.onControllerStateChanged(changedJob); + } + } + break; } } } @@ -754,7 +816,8 @@ public final class FlexibilityController extends StateController { /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ private static final String FC_CONFIG_PREFIX = "fc_"; - static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility"; + @VisibleForTesting + static final String KEY_APPLIED_CONSTRAINTS = FC_CONFIG_PREFIX + "applied_constraints"; static final String KEY_DEADLINE_PROXIMITY_LIMIT = FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms"; static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE = @@ -770,7 +833,7 @@ public final class FlexibilityController extends StateController { static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms"; - static final boolean DEFAULT_FLEXIBILITY_ENABLED = false; + static final int DEFAULT_APPLIED_CONSTRAINTS = 0; @VisibleForTesting static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS; @VisibleForTesting @@ -783,11 +846,8 @@ public final class FlexibilityController extends StateController { @VisibleForTesting static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS; - /** - * If false the controller will not track new jobs - * and the flexibility constraint will always be satisfied. - */ - public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED; + /** Which constraints to apply/consider in flex policy. */ + public int APPLIED_CONSTRAINTS = DEFAULT_APPLIED_CONSTRAINTS; /** How close to a jobs' deadline all flexible constraints will be dropped. */ public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS; /** For jobs that lack a deadline, the time that will be used to drop all constraints by. */ @@ -811,16 +871,19 @@ public final class FlexibilityController extends StateController { public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { - case KEY_FLEXIBILITY_ENABLED: - FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED) - && mDeviceSupportsFlexConstraints; - if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) { - mFlexibilityEnabled = FLEXIBILITY_ENABLED; + case KEY_APPLIED_CONSTRAINTS: + APPLIED_CONSTRAINTS = + properties.getInt(key, DEFAULT_APPLIED_CONSTRAINTS) + & mSupportedFlexConstraints; + if (mAppliedConstraints != APPLIED_CONSTRAINTS) { + mAppliedConstraints = APPLIED_CONSTRAINTS; mShouldReevaluateConstraints = true; - if (mFlexibilityEnabled) { + if (mAppliedConstraints != 0) { + mFlexibilityEnabled = true; mPrefetchController .registerPrefetchChangedListener(mPrefetchChangedListener); } else { + mFlexibilityEnabled = false; mPrefetchController .unRegisterPrefetchChangedListener(mPrefetchChangedListener); } @@ -893,7 +956,7 @@ public final class FlexibilityController extends StateController { private int[] parsePercentToDropString(String s) { String[] dropPercentString = s.split(","); - int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS]; + int[] dropPercentInt = new int[Integer.bitCount(FLEXIBLE_CONSTRAINTS)]; if (dropPercentInt.length != dropPercentString.length) { return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; } @@ -922,7 +985,7 @@ public final class FlexibilityController extends StateController { pw.println(":"); pw.increaseIndent(); - pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println(); + pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println(); pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println(); pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println(); pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS, 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 d1f575ef40c8..0d5d11e98860 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 @@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; -import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; import android.annotation.ElapsedRealtimeLong; @@ -155,7 +154,7 @@ public final class JobStatus { /** * Keeps track of how many flexible constraints must be satisfied for the job to execute. */ - private final int mNumRequiredFlexibleConstraints; + private int mNumAppliedFlexibleConstraints; /** * Number of required flexible constraints that have been dropped. @@ -697,11 +696,7 @@ public final class JobStatus { && satisfiesMinWindowException && (numFailures + numSystemStops) != 1 && lacksSomeFlexibleConstraints) { - mNumRequiredFlexibleConstraints = - NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mCanApplyTransportAffinities ? 1 : 0); requiredConstraints |= CONSTRAINT_FLEXIBLE; - } else { - mNumRequiredFlexibleConstraints = 0; } this.requiredConstraints = requiredConstraints; @@ -1527,9 +1522,14 @@ public final class JobStatus { return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0; } + /** Returns the number of flexible job constraints being applied to the job. */ + public int getNumAppliedFlexibleConstraints() { + return mNumAppliedFlexibleConstraints; + } + /** Returns the number of flexible job constraints required to be satisfied to execute */ public int getNumRequiredFlexibleConstraints() { - return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints; + return mNumAppliedFlexibleConstraints - mNumDroppedFlexibleConstraints; } /** @@ -2112,9 +2112,14 @@ public final class JobStatus { } /** Adjusts the number of required flexible constraints by the given number */ - public void adjustNumRequiredFlexibleConstraints(int adjustment) { - mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints, - mNumDroppedFlexibleConstraints - adjustment)); + public void setNumAppliedFlexibleConstraints(int count) { + mNumAppliedFlexibleConstraints = count; + } + + /** Sets the number of dropped flexible constraints to the given number */ + public void setNumDroppedFlexibleConstraints(int count) { + mNumDroppedFlexibleConstraints = Math.max(0, + Math.min(mNumAppliedFlexibleConstraints, count)); } /** 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 1c29982dbd48..8ddbf691359f 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 @@ -36,6 +36,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.UidObserver; +import android.app.job.JobInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.UsageEventListener; @@ -772,18 +773,23 @@ public final class QuotaController extends StateController { if (!jobStatus.shouldTreatAsExpeditedJob()) { // 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()) + if (mService.isBatteryCharging()) { + return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + } + // 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 + final boolean isInPrivilegedState = mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) - || isUidInForeground(jobStatus.getSourceUid())) { + || isUidInForeground(jobStatus.getSourceUid()); + final boolean isJobImportant = jobStatus.getEffectivePriority() >= JobInfo.PRIORITY_HIGH + || (jobStatus.getFlags() & JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0; + if (isInPrivilegedState && isJobImportant) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } return getTimeUntilQuotaConsumedLocked( @@ -2549,7 +2555,25 @@ public final class QuotaController extends StateController { */ @Override public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { - mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event).sendToTarget(); + // Skip posting a message to the handler for events we don't care about. + switch (event.getEventType()) { + case UsageEvents.Event.ACTIVITY_RESUMED: + case UsageEvents.Event.ACTIVITY_PAUSED: + case UsageEvents.Event.ACTIVITY_STOPPED: + case UsageEvents.Event.ACTIVITY_DESTROYED: + case UsageEvents.Event.USER_INTERACTION: + case UsageEvents.Event.CHOOSER_ACTION: + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case UsageEvents.Event.NOTIFICATION_SEEN: + mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event) + .sendToTarget(); + break; + default: + if (DEBUG) { + Slog.d(TAG, "Dropping event " + event.getEventType()); + } + break; + } } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java index b8397d2cd1b4..357e139617ef 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -316,8 +316,25 @@ public class InternalResourceService extends SystemService { */ @Override public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { - mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event) - .sendToTarget(); + // Skip posting a message to the handler for events we don't care about. + switch (event.getEventType()) { + case UsageEvents.Event.ACTIVITY_RESUMED: + case UsageEvents.Event.ACTIVITY_PAUSED: + case UsageEvents.Event.ACTIVITY_STOPPED: + case UsageEvents.Event.ACTIVITY_DESTROYED: + case UsageEvents.Event.USER_INTERACTION: + case UsageEvents.Event.CHOOSER_ACTION: + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + case UsageEvents.Event.NOTIFICATION_SEEN: + mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event) + .sendToTarget(); + break; + default: + if (DEBUG) { + Slog.d(TAG, "Dropping event " + event.getEventType()); + } + break; + } } }; diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 9ffb70496c59..43caaecebdaf 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -23,35 +23,43 @@ import java.io.FileWriter /** Usage: extract-flagged-apis <api text file> <output .pb file> */ fun main(args: Array<String>) { var cb = ApiFile.parseApi(listOf(File(args[0]))) - val flagToApi = mutableMapOf<String, MutableList<String>>() - cb.getPackages() - .allClasses() - .filter { it.methods().size > 0 } - .forEach { - for (method in it.methods()) { - val flagValue = - method.modifiers - .findAnnotation("android.annotation.FlaggedApi") - ?.findAttribute("value") - ?.value - ?.value() - if (flagValue != null && flagValue is String) { - val methodQualifiedName = "${it.qualifiedName()}.${method.name()}" - if (flagToApi.containsKey(flagValue)) { - flagToApi.get(flagValue)?.add(methodQualifiedName) - } else { - flagToApi.put(flagValue, mutableListOf(methodQualifiedName)) + var builder = FlagApiMap.newBuilder() + for (pkg in cb.getPackages().packages) { + var packageName = pkg.qualifiedName() + pkg.allClasses() + .filter { it.methods().size > 0 } + .forEach { + for (method in it.methods()) { + val flagValue = + method.modifiers + .findAnnotation("android.annotation.FlaggedApi") + ?.findAttribute("value") + ?.value + ?.value() + if (flagValue != null && flagValue is String) { + var api = + JavaMethod.newBuilder() + .setPackageName(packageName) + .setClassName(it.fullName()) + .setMethodName(method.name()) + for (param in method.parameters()) { + api.addParameterTypes(param.type().toTypeString()) + } + if (builder.containsFlagToApi(flagValue)) { + var updatedApis = + builder + .getFlagToApiOrThrow(flagValue) + .toBuilder() + .addJavaMethods(api) + .build() + builder.putFlagToApi(flagValue, updatedApis) + } else { + var apis = FlaggedApis.newBuilder().addJavaMethods(api).build() + builder.putFlagToApi(flagValue, apis) + } } } } - } - var builder = FlagApiMap.newBuilder() - for (flag in flagToApi.keys) { - var flaggedApis = FlaggedApis.newBuilder() - for (method in flagToApi.get(flag).orEmpty()) { - flaggedApis.addFlaggedApi(FlaggedApi.newBuilder().setQualifiedName(method)) - } - builder.putFlagToApi(flag, flaggedApis.build()) } val flagApiMap = builder.build() FileWriter(args[1]).use { it.write(flagApiMap.toString()) } diff --git a/api/coverage/tools/extract_flagged_apis.proto b/api/coverage/tools/extract_flagged_apis.proto index a858108a27b2..031d621b178f 100644 --- a/api/coverage/tools/extract_flagged_apis.proto +++ b/api/coverage/tools/extract_flagged_apis.proto @@ -25,10 +25,13 @@ message FlagApiMap { } message FlaggedApis { - repeated FlaggedApi flagged_api = 1; + repeated JavaMethod java_methods = 1; } -message FlaggedApi { - string qualified_name = 1; +message JavaMethod { + string package_name = 1; + string class_name = 2; + string method_name = 3; + repeated string parameter_types = 4; } diff --git a/core/api/current.txt b/core/api/current.txt index 7731face84f5..d490c3f4d17c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1216,6 +1216,7 @@ package android { field public static final int opticalInsetLeft = 16844168; // 0x1010588 field public static final int opticalInsetRight = 16844170; // 0x101058a field public static final int opticalInsetTop = 16844169; // 0x1010589 + field @FlaggedApi("android.content.pm.sdk_lib_independence") public static final int optional; field public static final int order = 16843242; // 0x10101ea field public static final int orderInCategory = 16843231; // 0x10101df field public static final int ordering = 16843490; // 0x10102e2 @@ -4467,7 +4468,7 @@ package android.app { method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int); method @CallSuper protected void onRestart(); method protected void onRestoreInstanceState(@NonNull android.os.Bundle); method public void onRestoreInstanceState(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle); @@ -4509,14 +4510,14 @@ package android.app { method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent); method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver<java.lang.Void,java.lang.Throwable>); method public final void requestPermissions(@NonNull String[], int); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public final void requestPermissions(@NonNull String[], int, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public final void requestPermissions(@NonNull String[], int, int); method public final void requestShowKeyboardShortcuts(); method @Deprecated public boolean requestVisibleBehind(boolean); method public final boolean requestWindowFeature(int); method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int); method public final void runOnUiThread(Runnable); method public void setActionBar(@Nullable android.widget.Toolbar); - method public void setAllowCrossUidActivitySwitchFromBelow(boolean); + method @FlaggedApi("android.security.asm_restrictions_enabled") public void setAllowCrossUidActivitySwitchFromBelow(boolean); method public void setContentTransitionManager(android.transition.TransitionManager); method public void setContentView(@LayoutRes int); method public void setContentView(android.view.View); @@ -4557,7 +4558,7 @@ package android.app { method public void setVrModeEnabled(boolean, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean shouldDockBigOverlays(); method public boolean shouldShowRequestPermissionRationale(@NonNull String); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public boolean shouldShowRequestPermissionRationale(@NonNull String, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public boolean shouldShowRequestPermissionRationale(@NonNull String, int); method public boolean shouldUpRecreateTask(android.content.Intent); method public boolean showAssist(android.os.Bundle); method @Deprecated public final void showDialog(int); @@ -9706,9 +9707,9 @@ package android.companion { method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public final void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException; method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Deprecated @MainThread public void onDeviceAppeared(@NonNull String); - method @Deprecated @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); + method @MainThread public void onDeviceAppeared(@NonNull android.companion.AssociationInfo); method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull String); - method @Deprecated @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); + method @MainThread public void onDeviceDisappeared(@NonNull android.companion.AssociationInfo); method @FlaggedApi("android.companion.device_presence") @MainThread public void onDeviceEvent(@NonNull android.companion.AssociationInfo, int); field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_APPEARED = 0; // 0x0 field @FlaggedApi("android.companion.device_presence") public static final int DEVICE_EVENT_BLE_DISAPPEARED = 1; // 0x1 @@ -9838,7 +9839,7 @@ package android.content { method public int describeContents(); method public void enforceCallingUid(); method @Nullable public String getAttributionTag(); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public int getDeviceId(); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public int getDeviceId(); method @Nullable public android.content.AttributionSource getNext(); method @Nullable public String getPackageName(); method public int getPid(); @@ -9854,7 +9855,7 @@ package android.content { ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource); method @NonNull public android.content.AttributionSource build(); method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull public android.content.AttributionSource.Builder setDeviceId(int); method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource); method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource); method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String); @@ -18787,6 +18788,7 @@ package android.hardware.camera2 { method @NonNull public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys(); method @NonNull public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys(); method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys(); + method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys(); method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys(); method @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys(); method @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission(); @@ -18811,6 +18813,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE; + field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Float>> CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB; @@ -18939,6 +18942,7 @@ package android.hardware.camera2 { method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException; method @NonNull public abstract String getId(); + method @FlaggedApi("com.android.internal.camera.flags.feature_combination_query") @NonNull public android.hardware.camera2.CameraCharacteristics getSessionCharacteristics(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; method @Deprecated public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException; method public void setCameraAudioRestriction(int) throws android.hardware.camera2.CameraAccessException; field public static final int AUDIO_RESTRICTION_NONE = 0; // 0x0 @@ -19098,6 +19102,7 @@ package android.hardware.camera2 { field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH = 2; // 0x2 field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; // 0x4 field public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5; // 0x5 + field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY = 6; // 0x6 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1 @@ -19163,6 +19168,8 @@ package android.hardware.camera2 { field public static final int CONTROL_EXTENDED_SCENE_MODE_BOKEH_CONTINUOUS = 2; // 0x2 field public static final int CONTROL_EXTENDED_SCENE_MODE_BOKEH_STILL_CAPTURE = 1; // 0x1 field public static final int CONTROL_EXTENDED_SCENE_MODE_DISABLED = 0; // 0x0 + field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE = 1; // 0x1 + field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") public static final int CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE = 0; // 0x0 field public static final int CONTROL_MODE_AUTO = 1; // 0x1 field public static final int CONTROL_MODE_OFF = 0; // 0x0 field public static final int CONTROL_MODE_OFF_KEEP_STATE = 3; // 0x3 @@ -19483,6 +19490,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EFFECT_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EXTENDED_SCENE_MODE; + field @FlaggedApi("com.android.internal.camera.flags.camera_ae_mode_low_light_boost") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_LOW_LIGHT_BOOST_STATE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE; @@ -36903,6 +36911,7 @@ package android.provider { field public static final String ACTION_REGIONAL_PREFERENCES_SETTINGS = "android.settings.REGIONAL_PREFERENCES_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA"; + field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = "android.settings.REQUEST_MEDIA_ROUTING_CONTROL"; field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; @@ -39298,7 +39307,7 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityStart(); method @NonNull public String getKeystoreAlias(); method public int getMaxUsageCount(); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); @@ -39306,7 +39315,7 @@ package android.security.keystore { method public boolean isDevicePropertiesAttestationIncluded(); method @NonNull public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isUnlockedDeviceRequired(); @@ -39338,7 +39347,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); @@ -39443,14 +39452,14 @@ package android.security.keystore { method @Nullable public java.util.Date getKeyValidityForOriginationEnd(); method @Nullable public java.util.Date getKeyValidityStart(); method public int getMaxUsageCount(); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests(); method public int getPurposes(); method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); method public int getUserAuthenticationValidityDurationSeconds(); method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified(); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified(); method public boolean isRandomizedEncryptionRequired(); method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); @@ -39472,7 +39481,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date); method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int); - method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...); + method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); @@ -43358,6 +43367,9 @@ package android.telephony { field public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl"; field public static final String KEY_MMS_USER_AGENT_STRING = "userAgent"; field public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY = "ntn_lte_rsrp_thresholds_int_array"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "ntn_lte_rsrq_thresholds_int_array"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "ntn_lte_rssnr_thresholds_int_array"; field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array"; field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool"; @@ -43372,6 +43384,7 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long"; field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long"; + field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = "parameters_used_for_ntn_lte_signal_bar_int"; field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; field public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT = "premium_capability_maximum_daily_notification_count_int"; @@ -53868,9 +53881,11 @@ package android.view { method @Deprecated public android.view.Display getDefaultDisplay(); method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics(); method public default boolean isCrossWindowBlurEnabled(); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer); method public void removeViewImmediate(android.view.View); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"; field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"; @@ -60851,6 +60866,16 @@ package android.window { method public void markSyncReady(); } + @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public final class TrustedPresentationThresholds implements android.os.Parcelable { + ctor @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public int describeContents(); + method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @NonNull public static final android.os.Parcelable.Creator<android.window.TrustedPresentationThresholds> CREATOR; + field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minAlpha; + field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minFractionRendered; + field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public final int stabilityRequirementMs; + } + } package javax.microedition.khronos.egl { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 378a18c175be..9a65388c2741 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -251,6 +251,7 @@ package android { field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"; + field @FlaggedApi("com.android.input.flags.override_key_behavior_permission_apis") public static final String OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW = "android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD"; field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS"; @@ -417,6 +418,7 @@ package android { field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600 field public static final int gameSessionService = 16844373; // 0x1010655 field public static final int hotwordDetectionService = 16844326; // 0x1010626 + field @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public static final int isVirtualDeviceOnly; field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int minExtensionVersion = 16844305; // 0x1010611 field public static final int playHomeTransitionSound = 16844358; // 0x1010646 @@ -3230,6 +3232,7 @@ package android.companion.virtual { method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName); method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener); method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int); + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDisplayImePolicy(int, int); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback); } @@ -3246,6 +3249,7 @@ package android.companion.virtual { method @Deprecated public int getDefaultNavigationPolicy(); method public int getDevicePolicy(int); method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent(); + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @Nullable public android.content.ComponentName getInputMethodComponent(); method public int getLockState(); method @Nullable public String getName(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); @@ -3279,6 +3283,7 @@ package android.companion.virtual { method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int); method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName); + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setInputMethodComponent(@Nullable android.content.ComponentName); method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); @@ -4004,7 +4009,7 @@ package android.content.pm { field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc field public static final int DELETE_KEEP_DATA = 1; // 0x1 field public static final int DELETE_SUCCEEDED = 1; // 0x1 - field @FlaggedApi("android.permission.flags.device_aware_permission_apis") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; + field @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; @@ -4085,7 +4090,8 @@ package android.content.pm { field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 - field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 + field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 + field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 @@ -4109,7 +4115,7 @@ package android.content.pm { public static interface PackageManager.OnPermissionsChangedListener { method public void onPermissionsChanged(int); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public default void onPermissionsChanged(int, @NonNull String); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public default void onPermissionsChanged(int, @NonNull String); } public static final class PackageManager.UninstallCompleteCallback implements android.os.Parcelable { @@ -4191,12 +4197,15 @@ package android.content.pm { public final class UserProperties implements android.os.Parcelable { method public int describeContents(); + method public int getCrossProfileContentSharingStrategy(); method public int getShowInQuietMode(); method public int getShowInSharingSurfaces(); method public boolean isCredentialShareableWithParent(); method public boolean isMediaSharedWithParent(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR; + field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1 + field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0 field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2 field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1 field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0 @@ -4532,6 +4541,7 @@ package android.hardware.camera2.extension { @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final class RequestProcessor.Request { ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public RequestProcessor.Request(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>>, int); + method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>> getParameters(); } @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface RequestProcessor.RequestCallback { @@ -10917,13 +10927,13 @@ package android.permission { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer); method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable); method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int); method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>); method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable); - method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable); + method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable); method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); @@ -17169,6 +17179,9 @@ package android.view { method @NonNull public default java.util.List<android.content.ComponentName> notifyScreenshotListeners(int); method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback); method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback); + field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1 + field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2 + field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0 } public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable { @@ -17289,6 +17302,10 @@ package android.view.displayhash { package android.view.inputmethod { + public final class InputMethodInfo implements android.os.Parcelable { + method @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") public boolean isVirtualDeviceOnly(); + } + public final class InputMethodManager { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfoAsUser(@NonNull android.os.UserHandle); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 98a78cfaa38c..39f2737dc880 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -259,6 +259,7 @@ package android.app { field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source"; field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls"; field public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword"; + field public static final String OPSTR_RESERVED_FOR_TESTING = "android:reserved_for_testing"; field public static final String OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android:use_icc_auth_with_device_identifier"; field public static final int OP_COARSE_LOCATION = 0; // 0x0 field public static final int OP_RECORD_AUDIO = 27; // 0x1b @@ -902,7 +903,7 @@ package android.content { ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder); ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource); ctor @FlaggedApi("android.permission.flags.attribution_source_constructor") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource); - ctor @FlaggedApi("android.permission.flags.device_aware_permission_apis") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource); + ctor @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource); method public void enforceCallingPid(); } @@ -1165,6 +1166,7 @@ package android.content.pm { public static final class UserProperties.Builder { ctor public UserProperties.Builder(); method @NonNull public android.content.pm.UserProperties build(); + method @NonNull public android.content.pm.UserProperties.Builder setCrossProfileContentSharingStrategy(int); method @NonNull public android.content.pm.UserProperties.Builder setShowInQuietMode(int); method @NonNull public android.content.pm.UserProperties.Builder setShowInSharingSurfaces(int); } @@ -3617,9 +3619,6 @@ package android.view { method public default void setShouldShowWithInsecureKeyguard(int, boolean); method public default boolean shouldShowSystemDecors(int); method @Nullable public default android.graphics.Bitmap snapshotTaskForRecents(@IntRange(from=0) int); - field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1 - field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2 - field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0 field public static final int LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP = 600; // 0x258 } diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 8e01779c6fac..15e29c2499f0 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -150,12 +150,13 @@ public final class AccessibilityGestureEvent implements Parcelable { private final int mDisplayId; private List<MotionEvent> mMotionEvents = new ArrayList<>(); -/** - * Constructs an AccessibilityGestureEvent to be dispatched to an accessibility service. - * @param gestureId the id number of the gesture. - * @param displayId the display on which this gesture was performed. - * @param motionEvents the motion events that lead to this gesture. - */ + /** + * Constructs an AccessibilityGestureEvent to be dispatched to an accessibility service. + * + * @param gestureId the id number of the gesture. + * @param displayId the display on which this gesture was performed. + * @param motionEvents the motion events that lead to this gesture. + */ public AccessibilityGestureEvent( int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) { mGestureId = gestureId; @@ -205,6 +206,29 @@ public final class AccessibilityGestureEvent implements Parcelable { return mMotionEvents; } + /** + * When we asynchronously use {@link AccessibilityGestureEvent}, we should make a copy, + * because motionEvent may be recycled before we use async. + * + * @hide + */ + @NonNull + public AccessibilityGestureEvent copyForAsync() { + return new AccessibilityGestureEvent(mGestureId, mDisplayId, + mMotionEvents.stream().map(MotionEvent::copy).toList()); + } + + /** + * After we use {@link AccessibilityGestureEvent} asynchronously, we should recycle the + * MotionEvent, avoid memory leaks. + * + * @hide + */ + public void recycle() { + mMotionEvents.forEach(MotionEvent::recycle); + mMotionEvents.clear(); + } + @NonNull @Override public String toString() { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index c52d27ea6608..ffed40538702 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5572,7 +5572,7 @@ public class Activity extends ContextThemeWrapper * @see #shouldShowRequestPermissionRationale * @see Context#DEVICE_ID_DEFAULT */ - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public final void requestPermissions(@NonNull String[] permissions, int requestCode, int deviceId) { if (requestCode < 0) { @@ -5645,7 +5645,7 @@ public class Activity extends ContextThemeWrapper * * @see #requestPermissions */ - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults, int deviceId) { onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -5678,7 +5678,7 @@ public class Activity extends ContextThemeWrapper * @see #requestPermissions * @see #onRequestPermissionsResult */ - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) { final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager() : createDeviceContext(deviceId).getPackageManager(); @@ -9387,6 +9387,7 @@ public class Activity extends ContextThemeWrapper * @param allowed {@code true} to disable the UID restrictions; {@code false} to revert back to * the default behaviour */ + @FlaggedApi(android.security.Flags.FLAG_ASM_RESTRICTIONS_ENABLED) public void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) { ActivityClient.getInstance().setAllowCrossUidActivitySwitchFromBelow(mToken, allowed); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index adaaee200895..8b39ed6fb411 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1544,11 +1544,12 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin, boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, - boolean dumpUnreachable, String[] args) { + boolean dumpUnreachable, boolean dumpAllocatorStats, String[] args) { FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); PrintWriter pw = new FastPrintWriter(fout); try { - dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable); + dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, + dumpUnreachable, dumpAllocatorStats); } finally { pw.flush(); IoUtils.closeQuietly(pfd); @@ -1557,7 +1558,8 @@ public final class ActivityThread extends ClientTransactionHandler @NeverCompile private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) { + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + boolean dumpUnreachable, boolean dumpAllocatorStats) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -1710,6 +1712,9 @@ public final class ActivityThread extends ClientTransactionHandler pw.println(" Unreachable memory"); pw.print(Debug.getUnreachableMemory(100, showContents)); } + if (dumpAllocatorStats) { + Debug.logAllocatorStats(); + } } @NeverCompile diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 71fe47e7b949..ec43184bd42c 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,8 +16,8 @@ package android.app; -import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED; import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER; +import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED; import static java.lang.Long.max; @@ -1521,9 +1521,17 @@ public class AppOpsManager { */ public static final int OP_MEDIA_ROUTING_CONTROL = AppProtoEnums.APP_OP_MEDIA_ROUTING_CONTROL; + /** + * Op code for use by tests to avoid interfering history logs that the wider system might + * trigger. + * + * @hide + */ + public static final int OP_RESERVED_FOR_TESTING = AppProtoEnums.APP_OP_RESERVED_FOR_TESTING; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 141; + public static final int _NUM_OP = 142; /** * All app ops represented as strings. @@ -1671,6 +1679,7 @@ public class AppOpsManager { OPSTR_CREATE_ACCESSIBILITY_OVERLAY, OPSTR_MEDIA_ROUTING_CONTROL, OPSTR_ENABLE_MOBILE_DATA_BY_USER, + OPSTR_RESERVED_FOR_TESTING, }) public @interface AppOpString {} @@ -2330,6 +2339,17 @@ public class AppOpsManager { public static final String OPSTR_ENABLE_MOBILE_DATA_BY_USER = "android:enable_mobile_data_by_user"; + /** + * Reserved for use by appop tests so that operations done legitimately by the platform don't + * interfere with expected results. Platform code should never use this. + * + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") + public static final String OPSTR_RESERVED_FOR_TESTING = + "android:reserved_for_testing"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -2887,6 +2907,8 @@ public class AppOpsManager { .setPermission(Manifest.permission.MEDIA_ROUTING_CONTROL).build(), new AppOpInfo.Builder(OP_ENABLE_MOBILE_DATA_BY_USER, OPSTR_ENABLE_MOBILE_DATA_BY_USER, "ENABLE_MOBILE_DATA_BY_USER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_RESERVED_FOR_TESTING, OPSTR_RESERVED_FOR_TESTING, + "OP_RESERVED_FOR_TESTING").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 75d8c1012e27..5541e7aef160 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -129,7 +129,7 @@ oneway interface IApplicationThread { void scheduleTrimMemory(int level); void dumpMemInfo(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem, boolean checkin, boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable, - in String[] args); + boolean dumpAllocatorLogs, in String[] args); void dumpMemInfoProto(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem, boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable, in String[] args); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 6aad1682466d..8b8576a0b25e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -683,10 +683,11 @@ public class KeyguardManager { * <p> * Specifically, this returns {@code true} if at least one of the following is true: * <ul> - * <li>The {@link Context}'s user has a secure lock screen. A full user has a secure lock - * screen if its lock screen is set to PIN, pattern, or password, as opposed to swipe or none. - * A profile that uses a unified challenge is considered to have a secure lock screen if and - * only if its parent user has a secure lock screen.</li> + * <li>The {@link Context}'s user has a secure lock screen. A full user or a profile that uses + * a separate challenge has a secure lock screen if its lock screen is set to PIN, pattern, or + * password, as opposed to swipe or none. A profile that uses a unified challenge is + * considered to have a secure lock screen if and only if its parent user has a secure lock + * screen.</li> * <li>At least one SIM card is currently locked and requires a PIN.</li> * </ul> * <p> @@ -733,8 +734,15 @@ public class KeyguardManager { * <p> * For a user that is not the current user but can be switched to (usually this means "another * full user"), and that has a PIN, pattern, or password, the device is always considered - * locked. For a profile with a unified challenge, the device is considered locked if and only - * if the device is locked for the parent user. + * locked. + * <p> + * For a profile with a unified challenge, the device locked state is the same as that of the + * parent user. + * <p> + * For a profile with a separate challenge, the device becomes unlocked when the profile's PIN, + * pattern, password, or biometric is verified. It becomes locked when the parent user becomes + * locked, the screen turns off, the device reboots, the device policy controller locks the + * profile, or the timeout set by the device policy controller expires. * * @return {@code true} if the device is currently locked for the user * @see #isKeyguardLocked() @@ -770,9 +778,10 @@ public class KeyguardManager { * Returns whether the user has a secure lock screen. * <p> * This returns {@code true} if the {@link Context}'s user has a secure lock screen. A full user - * has a secure lock screen if its lock screen is set to PIN, pattern, or password, as opposed - * to swipe or none. A profile that uses a unified challenge is considered to have a secure lock - * screen if and only if its parent user has a secure lock screen. + * or a profile that uses a separate challenge has a secure lock screen if its lock screen is + * set to PIN, pattern, or password, as opposed to swipe or none. A profile that uses a unified + * challenge is considered to have a secure lock screen if and only if its parent user has a + * secure lock screen. * <p> * This method does not consider whether the lock screen is currently showing or not. * <p> diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 1e538c52e635..90a265937082 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16640,6 +16640,7 @@ public class DevicePolicyManager { == DEVICE_OWNER_TYPE_FINANCED; } + // TODO(b/315298076): revert ag/25574027 and update the doc /** * Called by a device owner or profile owner of an organization-owned managed profile to enable * or disable USB data signaling for the device. When disabled, USB data connections @@ -16649,12 +16650,11 @@ public class DevicePolicyManager { * {@link #canUsbDataSignalingBeDisabled()} to check whether enabling or disabling USB data * signaling is supported on the device. * - * Starting from {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, after the USB data signaling + * Starting from Android 15, after the USB data signaling * policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String, * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was * successfully set or not. This callback will contain: * <ul> - * li> The policy identifier {@link DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY} * <li> The {@link TargetUser} that this policy relates to * <li> The {@link PolicyUpdateResult}, which will be * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index c99a45764de7..4d0267ca0cbb 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -40,7 +40,6 @@ import java.util.concurrent.Executor; /** * A service that receives calls from the system with device events. - * See {@link #onDeviceEvent(AssociationInfo, int)}. * * <p> * Companion applications must create a service that {@code extends} @@ -311,10 +310,7 @@ public abstract class CompanionDeviceService extends Service { * Called by system whenever a device associated with this app is connected. * * @param associationInfo A record for the companion device. - * - * @deprecated please override {@link #onDeviceEvent(AssociationInfo, int)} instead. */ - @Deprecated @MainThread public void onDeviceAppeared(@NonNull AssociationInfo associationInfo) { if (!associationInfo.isSelfManaged()) { @@ -326,10 +322,7 @@ public abstract class CompanionDeviceService extends Service { * Called by system whenever a device associated with this app is disconnected. * * @param associationInfo A record for the companion device. - * - * @deprecated please override {@link #onDeviceEvent(AssociationInfo, int)} instead. */ - @Deprecated @MainThread public void onDeviceDisappeared(@NonNull AssociationInfo associationInfo) { if (!associationInfo.isSelfManaged()) { diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 3520c0b4d724..12229b12fb16 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -219,6 +219,10 @@ interface IVirtualDevice { @EnforcePermission("CREATE_VIRTUAL_DEVICE") void setShowPointerIcon(boolean showPointerIcon); + /** Sets an IME policy for the given display. */ + @EnforcePermission("CREATE_VIRTUAL_DEVICE") + void setDisplayImePolicy(int displayId, int policy); + /** * Registers an intent interceptor that will intercept an intent attempting to launch * when matching the provided IntentFilter and calls the callback with the intercepted diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java index 003dffb1f9c1..2abeeeecc1c6 100644 --- a/core/java/android/companion/virtual/VirtualDeviceInternal.java +++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java @@ -53,6 +53,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.ArrayMap; +import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; @@ -361,6 +362,14 @@ public class VirtualDeviceInternal { } } + void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { + try { + mVirtualDevice.setDisplayImePolicy(displayId, policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + void addActivityListener( @CallbackExecutor @NonNull Executor executor, @NonNull VirtualDeviceManager.ActivityListener listener) { diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 41c90b96dc84..eef60f11fb1c 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -63,6 +63,7 @@ import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.Surface; +import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; @@ -914,6 +915,24 @@ public final class VirtualDeviceManager { } /** + * Specifies the IME behavior on the given display. By default, all displays created by + * virtual devices have {@link WindowManager#DISPLAY_IME_POLICY_LOCAL}. + * + * @param displayId the ID of the display to change the IME policy for. It must be owned by + * this virtual device. + * @param policy the IME policy to use on that display + * @throws SecurityException if the display is not owned by this device or is not + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED trusted} + */ + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME) + public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { + if (Flags.vdmCustomIme()) { + mVirtualDeviceInternal.setDisplayImePolicy(displayId, policy); + } + } + + /** * Adds an activity listener to listen for events such as top activity change or virtual * display task stack became empty. * diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 0d73e44f5197..0253ddd93a44 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -258,6 +258,7 @@ public final class VirtualDeviceParams implements Parcelable { // Mapping of @PolicyType to @DevicePolicy @NonNull private final SparseIntArray mDevicePolicies; @Nullable private final ComponentName mHomeComponent; + @Nullable private final ComponentName mInputMethodComponent; @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs; @Nullable private final IVirtualSensorCallback mVirtualSensorCallback; private final int mAudioPlaybackSessionId; @@ -273,6 +274,7 @@ public final class VirtualDeviceParams implements Parcelable { @Nullable String name, @NonNull SparseIntArray devicePolicies, @Nullable ComponentName homeComponent, + @Nullable ComponentName inputMethodComponent, @NonNull List<VirtualSensorConfig> virtualSensorConfigs, @Nullable IVirtualSensorCallback virtualSensorCallback, int audioPlaybackSessionId, @@ -289,6 +291,7 @@ public final class VirtualDeviceParams implements Parcelable { mName = name; mDevicePolicies = Objects.requireNonNull(devicePolicies); mHomeComponent = homeComponent; + mInputMethodComponent = inputMethodComponent; mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs); mVirtualSensorCallback = virtualSensorCallback; mAudioPlaybackSessionId = audioPlaybackSessionId; @@ -312,6 +315,7 @@ public final class VirtualDeviceParams implements Parcelable { mAudioPlaybackSessionId = parcel.readInt(); mAudioRecordingSessionId = parcel.readInt(); mHomeComponent = parcel.readTypedObject(ComponentName.CREATOR); + mInputMethodComponent = parcel.readTypedObject(ComponentName.CREATOR); } /** @@ -336,6 +340,18 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Returns the custom component used as input method on all displays owned by this virtual + * device. + * + * @see Builder#setInputMethodComponent + */ + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME) + @Nullable + public ComponentName getInputMethodComponent() { + return mInputMethodComponent; + } + + /** * Returns the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. * @@ -532,6 +548,7 @@ public final class VirtualDeviceParams implements Parcelable { dest.writeInt(mAudioPlaybackSessionId); dest.writeInt(mAudioRecordingSessionId); dest.writeTypedObject(mHomeComponent, flags); + dest.writeTypedObject(mInputMethodComponent, flags); } @Override @@ -563,6 +580,8 @@ public final class VirtualDeviceParams implements Parcelable { && Objects.equals(mActivityPolicyExemptions, that.mActivityPolicyExemptions) && mDefaultActivityPolicy == that.mDefaultActivityPolicy && Objects.equals(mName, that.mName) + && Objects.equals(mHomeComponent, that.mHomeComponent) + && Objects.equals(mInputMethodComponent, that.mInputMethodComponent) && mAudioPlaybackSessionId == that.mAudioPlaybackSessionId && mAudioRecordingSessionId == that.mAudioRecordingSessionId; } @@ -572,7 +591,8 @@ public final class VirtualDeviceParams implements Parcelable { int hashCode = Objects.hash( mLockState, mUsersWithMatchingAccounts, mCrossTaskNavigationExemptions, mDefaultNavigationPolicy, mActivityPolicyExemptions, mDefaultActivityPolicy, mName, - mDevicePolicies, mHomeComponent, mAudioPlaybackSessionId, mAudioRecordingSessionId); + mDevicePolicies, mHomeComponent, mInputMethodComponent, mAudioPlaybackSessionId, + mAudioRecordingSessionId); for (int i = 0; i < mDevicePolicies.size(); i++) { hashCode = 31 * hashCode + mDevicePolicies.keyAt(i); hashCode = 31 * hashCode + mDevicePolicies.valueAt(i); @@ -593,6 +613,7 @@ public final class VirtualDeviceParams implements Parcelable { + " mName=" + mName + " mDevicePolicies=" + mDevicePolicies + " mHomeComponent=" + mHomeComponent + + " mInputMethodComponent=" + mInputMethodComponent + " mAudioPlaybackSessionId=" + mAudioPlaybackSessionId + " mAudioRecordingSessionId=" + mAudioRecordingSessionId + ")"; @@ -612,6 +633,8 @@ public final class VirtualDeviceParams implements Parcelable { pw.println(prefix + "mActivityPolicyExemptions=" + mActivityPolicyExemptions); pw.println(prefix + "mDevicePolicies=" + mDevicePolicies); pw.println(prefix + "mVirtualSensorConfigs=" + mVirtualSensorConfigs); + pw.println(prefix + "mHomeComponent=" + mHomeComponent); + pw.println(prefix + "mInputMethodComponent=" + mInputMethodComponent); pw.println(prefix + "mAudioPlaybackSessionId=" + mAudioPlaybackSessionId); pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId); } @@ -644,16 +667,17 @@ public final class VirtualDeviceParams implements Parcelable { private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED; private boolean mDefaultActivityPolicyConfigured = false; @Nullable private String mName; - @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray(); + @NonNull private final SparseIntArray mDevicePolicies = new SparseIntArray(); private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE; private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE; - @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>(); + @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>(); @Nullable private Executor mVirtualSensorCallbackExecutor; @Nullable private VirtualSensorCallback mVirtualSensorCallback; @Nullable private Executor mVirtualSensorDirectChannelCallbackExecutor; @Nullable private VirtualSensorDirectChannelCallback mVirtualSensorDirectChannelCallback; @Nullable private ComponentName mHomeComponent; + @Nullable private ComponentName mInputMethodComponent; private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub { @NonNull @@ -749,6 +773,28 @@ public final class VirtualDeviceParams implements Parcelable { } /** + * Specifies a component to be used as input method on all displays owned by this virtual + * device. + * + * @param inputMethodComponent The component name to be used as input method. Must comply to + * all general input method requirements described in the guide to + * <a href="{@docRoot}guide/topics/text/creating-input-method.html"> + * Creating an Input Method</a>. If the given component is not available for any user that + * may interact with the virtual device, then there will effectively be no IME on this + * device's displays for that user. + * + * @see android.inputmethodservice.InputMethodService + * @attr ref android.R.styleable#InputMethod_isVirtualDeviceOnly + * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker + */ + @FlaggedApi(Flags.FLAG_VDM_CUSTOM_IME) + @NonNull + public Builder setInputMethodComponent(@Nullable ComponentName inputMethodComponent) { + mInputMethodComponent = inputMethodComponent; + return this; + } + + /** * Sets the user handles with matching managed accounts on the remote device to which * this virtual device is streaming. The caller is responsible for verifying the presence * and legitimacy of a matching managed account on the remote device. @@ -1136,6 +1182,7 @@ public final class VirtualDeviceParams implements Parcelable { mName, mDevicePolicies, mHomeComponent, + mInputMethodComponent, mVirtualSensorConfigs, virtualSensorCallbackDelegate, mAudioPlaybackSessionId, diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index f0477d47f723..ce2490b8efb8 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -39,6 +39,13 @@ flag { } flag { + name: "vdm_custom_ime" + namespace: "virtual_devices" + description: "Enable custom IME API" + bug: "287269288" +} + +flag { name: "vdm_custom_home" namespace: "virtual_devices" description: "Enable custom home API" diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index b2074a6e7309..a1357c91b2cf 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -173,7 +173,7 @@ public final class AttributionSource implements Parcelable { /** @hide */ @TestApi - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public AttributionSource(int uid, int pid, @Nullable String packageName, @Nullable String attributionTag, @NonNull IBinder token, @Nullable String[] renouncedPermissions, @@ -539,7 +539,7 @@ public final class AttributionSource implements Parcelable { * <p> * This device ID is used for permissions checking during attribution source validation. */ - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public int getDeviceId() { return mAttributionSourceState.deviceId; } @@ -727,7 +727,7 @@ public final class AttributionSource implements Parcelable { * * @return the builder */ - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public @NonNull Builder setDeviceId(int deviceId) { checkNotUsed(); mBuilderFieldsSet |= 0x12; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index c86ccfdaa7d4..c7a75ed5ea9c 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -117,6 +117,7 @@ import java.util.Objects; * developer guide.</p> * </div> */ +@android.ravenwood.annotation.RavenwoodKeepPartialClass public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 { private static final String TAG = "ContentProvider"; @@ -2781,6 +2782,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep private Uri maybeGetUriWithoutUserId(Uri uri) { if (mSingleUser) { return uri; @@ -2789,6 +2791,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static int getUserIdFromAuthority(String auth, int defaultUserId) { if (auth == null) return defaultUserId; int end = auth.lastIndexOf('@'); @@ -2803,17 +2806,20 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static int getUserIdFromAuthority(String auth) { return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static int getUserIdFromUri(Uri uri, int defaultUserId) { if (uri == null) return defaultUserId; return getUserIdFromAuthority(uri.getAuthority(), defaultUserId); } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static int getUserIdFromUri(Uri uri) { return getUserIdFromUri(uri, UserHandle.USER_CURRENT); } @@ -2824,6 +2830,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * @hide */ @TestApi + @android.ravenwood.annotation.RavenwoodKeep public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) { return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier())); } @@ -2834,6 +2841,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * If there is no userId in the authority, it symply returns the argument * @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static String getAuthorityWithoutUserId(String auth) { if (auth == null) return null; int end = auth.lastIndexOf('@'); @@ -2841,6 +2849,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static Uri getUriWithoutUserId(Uri uri) { if (uri == null) return null; Uri.Builder builder = uri.buildUpon(); @@ -2849,6 +2858,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static boolean uriHasUserId(Uri uri) { if (uri == null) return false; return !TextUtils.isEmpty(uri.getUserInfo()); @@ -2872,6 +2882,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall */ @NonNull @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @android.ravenwood.annotation.RavenwoodKeep public static Uri createContentUriForUser( @NonNull Uri contentUri, @NonNull UserHandle userHandle) { if (!ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) { @@ -2898,6 +2909,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall /** @hide */ @UnsupportedAppUsage + @android.ravenwood.annotation.RavenwoodKeep public static Uri maybeAddUserId(Uri uri, int userId) { if (uri == null) return null; if (userId != UserHandle.USER_CURRENT diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 457fd63fa3d8..e395127dfaf3 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -379,7 +379,8 @@ public class PackageInstaller { /** * If true, the requestor of the unarchival has specified that the app should be unarchived - * for all users. + * for all users. Sent as part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} + * intent. */ @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_ALL_USERS = @@ -396,6 +397,9 @@ public class PackageInstaller { * with an intent for a corresponding follow-up action (e.g. storage clearing dialog) or a * failure dialog. * + * <p> Used as part of {@link #requestUnarchive} to return the status of the unarchival through + * the {@link IntentSender}. + * * @see #requestUnarchive */ @FlaggedApi(Flags.FLAG_ARCHIVING) @@ -704,7 +708,8 @@ public class PackageInstaller { /** * The installer responsible for the unarchival is disabled. * - * <p> Should only be used by the system. + * <p> The system will return this status if appropriate. Installers do not need to verify for + * this error. */ @FlaggedApi(Flags.FLAG_ARCHIVING) public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; @@ -712,7 +717,8 @@ public class PackageInstaller { /** * The installer responsible for the unarchival has been uninstalled * - * <p> Should only be used by the system. + * <p> The system will return this status if appropriate. Installers do not need to verify for + * this error. */ @FlaggedApi(Flags.FLAG_ARCHIVING) public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; @@ -1238,7 +1244,7 @@ public class PackageInstaller { * {@code statusReceiver} if timeout happens before commit. * @throws IllegalArgumentException if the {@code statusReceiver} from an immutable * {@link android.app.PendingIntent} when caller has a target SDK of API - * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above. + * 35 or above. */ public void commitSessionAfterInstallConstraintsAreMet(int sessionId, @NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints, @@ -1948,7 +1954,7 @@ public class PackageInstaller { * {@link #openWrite(String, long, long)} are still open. * @throws IllegalArgumentException if the {@code statusReceiver} from an immutable * {@link android.app.PendingIntent} when caller has a target SDK of API - * version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above. + * version 35 or above. * * @see android.app.admin.DevicePolicyManager * @see #requestUserPreapproval @@ -1979,7 +1985,7 @@ public class PackageInstaller { * individual status codes on how to handle them. * @throws IllegalArgumentException if the {@code statusReceiver} from an immutable * {@link android.app.PendingIntent} when caller has a target SDK of API - * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or above. + * 35 or above. * * @hide */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 607e9043e9bf..e2243292ab8d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -731,7 +731,7 @@ public abstract class PackageManager { * @see VirtualDeviceManager.VirtualDevice#getPersistentDeviceId() * @see VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT */ - @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) default void onPermissionsChanged(int uid, @NonNull String persistentDeviceId) { Objects.requireNonNull(persistentDeviceId); if (Objects.equals(persistentDeviceId, @@ -919,6 +919,10 @@ public abstract class PackageManager { @Retention(RetentionPolicy.SOURCE) public @interface InstrumentationInfoFlags {} + //------------------------------------------------------------------------- + // Beginning of GET_ and MATCH_ flags + //------------------------------------------------------------------------- + /** * {@link PackageInfo} flag: return information about * activities in the package in {@link PackageInfo#activities}. @@ -1216,30 +1220,21 @@ public abstract class PackageManager { */ public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000; + /** @hide */ + @Deprecated + public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; + /** - * {@link ResolveInfo} flag: allow matching components across clone profile - * <p> - * This flag is used only for query and not resolution, the default behaviour would be to - * restrict querying across clone profile. This flag would be honored only if caller have - * permission {@link Manifest.permission.QUERY_CLONED_APPS}. + * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead. * - * @hide - * <p> + * @hide */ + @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation + @Deprecated @SystemApi public static final int MATCH_CLONE_PROFILE = 0x20000000; /** - * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension. - */ - @Deprecated - public static final int GET_ATTRIBUTIONS = 0x80000000; - - /** @hide */ - @Deprecated - public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; - - /** * {@link PackageInfo} flag: include system apps that are in the uninstalled state and have * been set to be hidden until installed via {@link #setSystemAppState}. * @hide @@ -1257,6 +1252,12 @@ public abstract class PackageManager { public static final int MATCH_APEX = 0x40000000; /** + * @deprecated Use {@link #GET_ATTRIBUTIONS_LONG} to avoid unintended sign extension. + */ + @Deprecated + public static final int GET_ATTRIBUTIONS = 0x80000000; + + /** * {@link PackageInfo} flag: return all attributions declared in the package manifest */ public static final long GET_ATTRIBUTIONS_LONG = 0x80000000L; @@ -1282,6 +1283,23 @@ public abstract class PackageManager { public static final long MATCH_QUARANTINED_COMPONENTS = 1L << 33; /** + * {@link ResolveInfo} flag: allow matching components across clone profile + * <p> + * This flag is used only for query and not resolution, the default behaviour would be to + * restrict querying across clone profile. This flag would be honored only if caller have + * permission {@link Manifest.permission.QUERY_CLONED_APPS}. + * + * @hide + */ + @FlaggedApi(android.content.pm.Flags.FLAG_FIX_DUPLICATED_FLAGS) + @SystemApi + public static final long MATCH_CLONE_PROFILE_LONG = 1L << 34; + + //------------------------------------------------------------------------- + // End of GET_ and MATCH_ flags + //------------------------------------------------------------------------- + + /** * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when * resolving an intent that matches the {@code CrossProfileIntentFilter}, * the current profile will be skipped. Only activities in the target user @@ -4875,7 +4893,7 @@ public abstract class PackageManager { * @hide */ @SystemApi - @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID"; diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index fdd2aa1fe5fa..25ba72551b04 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -142,8 +142,10 @@ public final class SharedLibraryInfo implements Parcelable { mName = parcel.readString8(); mVersion = parcel.readLong(); mType = parcel.readInt(); - mDeclaringPackage = parcel.readParcelable(null, android.content.pm.VersionedPackage.class); - mDependentPackages = parcel.readArrayList(null, android.content.pm.VersionedPackage.class); + mDeclaringPackage = + parcel.readParcelable(null, android.content.pm.VersionedPackage.class); + mDependentPackages = + parcel.readArrayList(null, android.content.pm.VersionedPackage.class); mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR); mIsNative = parcel.readBoolean(); } diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java index 56e8291f25e9..57749d43eb37 100644 --- a/core/java/android/content/pm/UserProperties.java +++ b/core/java/android/content/pm/UserProperties.java @@ -68,6 +68,11 @@ public final class UserProperties implements Parcelable { "authAlwaysRequiredToDisableQuietMode"; private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent"; private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible"; + private static final String ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = + "allowStoppingUserWithDelayedLocking"; + + private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = + "crossProfileContentSharingStrategy"; /** Index values of each property (to indicate whether they are present in this object). */ @IntDef(prefix = "INDEX_", value = { @@ -86,6 +91,8 @@ public final class UserProperties implements Parcelable { INDEX_SHOW_IN_QUIET_MODE, INDEX_SHOW_IN_SHARING_SURFACES, INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE, + INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY, + INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING, }) @Retention(RetentionPolicy.SOURCE) private @interface PropertyIndex { @@ -105,6 +112,8 @@ public final class UserProperties implements Parcelable { private static final int INDEX_SHOW_IN_QUIET_MODE = 12; private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13; private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14; + private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15; + private static final int INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = 16; /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */ private long mPropertiesPresent = 0; @@ -365,6 +374,45 @@ public final class UserProperties implements Parcelable { */ @SuppressLint("UnflaggedApi") // b/306636213 public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO; + /** + * Possible values for cross profile content sharing strategy for this profile. + * + * @hide + */ + @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_STRATEGY_"}, value = { + CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION, + CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CrossProfileContentSharingStrategy { + } + + /** + * Signifies that cross-profile content sharing strategy, both to and from this profile, should + * not be delegated to any other user/profile. + * For ex: + * If this property is set for a profile, content sharing applications (such as Android + * Sharesheet), should not delegate the decision to share content between that profile and + * another profile to whether content sharing is allowed between any other profile/user related + * to those profiles. They should instead decide, based upon whether content sharing is + * specifically allowed between the two profiles in question. + */ + @SuppressLint("UnflaggedApi") // b/306636213 + public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; + + /** + * Signifies that cross-profile content sharing strategy, both to and from this profile, should + * be based upon the strategy used by the parent user of the profile. + * For ex: + * If this property is set for a profile A, content sharing applications (such as Android + * Sharesheet), should share content between profile A and profile B, based upon whether content + * sharing is allowed between the parent of profile A and profile B. + * If it's also set for profile B, then decision should, in turn be made by considering content + * sharing strategy between the parents of both profiles. + */ + @SuppressLint("UnflaggedApi") // b/306636213 + public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; + /** * Creates a UserProperties (intended for the SystemServer) that stores a reference to the given @@ -406,6 +454,7 @@ public final class UserProperties implements Parcelable { setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy()); setDeleteAppWithParent(orig.getDeleteAppWithParent()); setAlwaysVisible(orig.getAlwaysVisible()); + setAllowStoppingUserWithDelayedLocking(orig.getAllowStoppingUserWithDelayedLocking()); } if (hasManagePermission) { // Add items that require MANAGE_USERS or stronger. @@ -423,6 +472,7 @@ public final class UserProperties implements Parcelable { setCredentialShareableWithParent(orig.isCredentialShareableWithParent()); setShowInQuietMode(orig.getShowInQuietMode()); setShowInSharingSurfaces(orig.getShowInSharingSurfaces()); + setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy()); } /** @@ -680,6 +730,11 @@ public final class UserProperties implements Parcelable { this.mUpdateCrossProfileIntentFiltersOnOTA = val; setPresent(INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA); } + /** + Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during + OTA update between user-parent + */ + private boolean mUpdateCrossProfileIntentFiltersOnOTA; /** * Returns whether a profile shares media with its parent user. @@ -741,12 +796,38 @@ public final class UserProperties implements Parcelable { } private boolean mAuthAlwaysRequiredToDisableQuietMode; - /* - Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during - OTA update between user-parent + /** + * Returns whether a user (usually a profile) is allowed to leave the CE storage unlocked when + * stopped. + * + * <p> Setting this property to true will enable the user's CE storage to remain unlocked when + * the user is stopped using + * {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int, + * boolean, IStopUserCallback)}. + * + * <p> When this property is false, delayed locking may still be applicable at a global + * level for all users via the {@code config_multiuserDelayUserDataLocking}. That is, delayed + * locking for a user can happen if either the device configuration is set or if this property + * is set. When both, the config and the property value is false, the user storage is always + * locked when the user is stopped. + * @hide */ - private boolean mUpdateCrossProfileIntentFiltersOnOTA; - + public boolean getAllowStoppingUserWithDelayedLocking() { + if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) { + return mAllowStoppingUserWithDelayedLocking; + } + if (mDefaultProperties != null) { + return mDefaultProperties.mAllowStoppingUserWithDelayedLocking; + } + throw new SecurityException( + "You don't have permission to query allowStoppingUserWithDelayedLocking"); + } + /** @hide */ + public void setAllowStoppingUserWithDelayedLocking(boolean val) { + this.mAllowStoppingUserWithDelayedLocking = val; + setPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING); + } + private boolean mAllowStoppingUserWithDelayedLocking; /** * Returns the user's {@link CrossProfileIntentFilterAccessControlLevel}. @@ -776,8 +857,7 @@ public final class UserProperties implements Parcelable { private @CrossProfileIntentFilterAccessControlLevel int mCrossProfileIntentFilterAccessControl; /** - * Returns the user's {@link CrossProfileIntentResolutionStrategy}. If not explicitly - * configured, default value is {@link #CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT}. + * Returns the user's {@link CrossProfileIntentResolutionStrategy}. * @return user's {@link CrossProfileIntentResolutionStrategy}. * * @hide @@ -792,11 +872,8 @@ public final class UserProperties implements Parcelable { throw new SecurityException("You don't have permission to query " + "crossProfileIntentResolutionStrategy"); } - /** - * Sets {@link CrossProfileIntentResolutionStrategy} for the user. - * @param val resolution strategy for user - * @hide - */ + + /** @hide */ public void setCrossProfileIntentResolutionStrategy( @CrossProfileIntentResolutionStrategy int val) { this.mCrossProfileIntentResolutionStrategy = val; @@ -804,6 +881,39 @@ public final class UserProperties implements Parcelable { } private @CrossProfileIntentResolutionStrategy int mCrossProfileIntentResolutionStrategy; + /** + * Returns the user's {@link CrossProfileContentSharingStrategy}. + * + * Content sharing applications, such as Android Sharesheet allow sharing of content + * (an image, for ex.) between profiles, based upon cross-profile access checks between the + * originating and destined profile. + * In some cases however, we may want another user (such as profile parent) to serve as the + * delegated user to be used for such checks. + * To effect the same, clients can fetch this property and accordingly replace the + * originating/destined profile by another user for cross-profile access checks. + * + * @return user's {@link CrossProfileContentSharingStrategy}. + */ + @SuppressLint("UnflaggedApi") // b/306636213 + public @CrossProfileContentSharingStrategy int getCrossProfileContentSharingStrategy() { + if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) { + return mCrossProfileContentSharingStrategy; + } + if (mDefaultProperties != null) { + return mDefaultProperties.mCrossProfileContentSharingStrategy; + } + throw new SecurityException("You don't have permission to query " + + "crossProfileContentSharingStrategy"); + } + + /** @hide */ + public void setCrossProfileContentSharingStrategy( + @CrossProfileContentSharingStrategy int val) { + this.mCrossProfileContentSharingStrategy = val; + setPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY); + } + private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy; + @Override public String toString() { @@ -825,8 +935,11 @@ public final class UserProperties implements Parcelable { + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent() + ", mAuthAlwaysRequiredToDisableQuietMode=" + isAuthAlwaysRequiredToDisableQuietMode() + + ", mAllowStoppingUserWithDelayedLocking=" + + getAllowStoppingUserWithDelayedLocking() + ", mDeleteAppWithParent=" + getDeleteAppWithParent() + ", mAlwaysVisible=" + getAlwaysVisible() + + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy() + "}"; } @@ -854,8 +967,12 @@ public final class UserProperties implements Parcelable { + isCredentialShareableWithParent()); pw.println(prefix + " mAuthAlwaysRequiredToDisableQuietMode=" + isAuthAlwaysRequiredToDisableQuietMode()); + pw.println(prefix + " mAllowStoppingUserWithDelayedLocking=" + + getAllowStoppingUserWithDelayedLocking()); pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent()); pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible()); + pw.println(prefix + " mCrossProfileContentSharingStrategy=" + + getCrossProfileContentSharingStrategy()); } /** @@ -928,12 +1045,17 @@ public final class UserProperties implements Parcelable { case ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE: setAuthAlwaysRequiredToDisableQuietMode(parser.getAttributeBoolean(i)); break; + case ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING: + setAllowStoppingUserWithDelayedLocking(parser.getAttributeBoolean(i)); + break; case ATTR_DELETE_APP_WITH_PARENT: setDeleteAppWithParent(parser.getAttributeBoolean(i)); break; case ATTR_ALWAYS_VISIBLE: setAlwaysVisible(parser.getAttributeBoolean(i)); break; + case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY: + setCrossProfileContentSharingStrategy(parser.getAttributeInt(i)); default: Slog.w(LOG_TAG, "Skipping unknown property " + attributeName); } @@ -1000,6 +1122,10 @@ public final class UserProperties implements Parcelable { serializer.attributeBoolean(null, ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE, mAuthAlwaysRequiredToDisableQuietMode); } + if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) { + serializer.attributeBoolean(null, ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING, + mAllowStoppingUserWithDelayedLocking); + } if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) { serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT, mDeleteAppWithParent); @@ -1008,6 +1134,10 @@ public final class UserProperties implements Parcelable { serializer.attributeBoolean(null, ATTR_ALWAYS_VISIBLE, mAlwaysVisible); } + if (isPresent(INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY)) { + serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY, + mCrossProfileContentSharingStrategy); + } } // For use only with an object that has already had any permission-lacking fields stripped out. @@ -1027,8 +1157,10 @@ public final class UserProperties implements Parcelable { dest.writeBoolean(mMediaSharedWithParent); dest.writeBoolean(mCredentialShareableWithParent); dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode); + dest.writeBoolean(mAllowStoppingUserWithDelayedLocking); dest.writeBoolean(mDeleteAppWithParent); dest.writeBoolean(mAlwaysVisible); + dest.writeInt(mCrossProfileContentSharingStrategy); } /** @@ -1052,8 +1184,10 @@ public final class UserProperties implements Parcelable { mMediaSharedWithParent = source.readBoolean(); mCredentialShareableWithParent = source.readBoolean(); mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean(); + mAllowStoppingUserWithDelayedLocking = source.readBoolean(); mDeleteAppWithParent = source.readBoolean(); mAlwaysVisible = source.readBoolean(); + mCrossProfileContentSharingStrategy = source.readInt(); } @Override @@ -1098,8 +1232,11 @@ public final class UserProperties implements Parcelable { private boolean mMediaSharedWithParent = false; private boolean mCredentialShareableWithParent = false; private boolean mAuthAlwaysRequiredToDisableQuietMode = false; + private boolean mAllowStoppingUserWithDelayedLocking = false; private boolean mDeleteAppWithParent = false; private boolean mAlwaysVisible = false; + private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy = + CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION; /** * @hide @@ -1215,6 +1352,16 @@ public final class UserProperties implements Parcelable { return this; } + /** Sets the value for {@link #mAllowStoppingUserWithDelayedLocking} + * @hide + */ + public Builder setAllowStoppingUserWithDelayedLocking( + boolean allowStoppingUserWithDelayedLocking) { + mAllowStoppingUserWithDelayedLocking = + allowStoppingUserWithDelayedLocking; + return this; + } + /** Sets the value for {@link #mDeleteAppWithParent} * @hide */ @@ -1231,6 +1378,19 @@ public final class UserProperties implements Parcelable { return this; } + /** Sets the value for {@link #mCrossProfileContentSharingStrategy} + * @hide + */ + + @TestApi + @SuppressLint("UnflaggedApi") // b/306636213 + @NonNull + public Builder setCrossProfileContentSharingStrategy(@CrossProfileContentSharingStrategy + int crossProfileContentSharingStrategy) { + mCrossProfileContentSharingStrategy = crossProfileContentSharingStrategy; + return this; + } + /** Builds a UserProperties object with *all* values populated. * @hide */ @@ -1252,8 +1412,10 @@ public final class UserProperties implements Parcelable { mMediaSharedWithParent, mCredentialShareableWithParent, mAuthAlwaysRequiredToDisableQuietMode, + mAllowStoppingUserWithDelayedLocking, mDeleteAppWithParent, - mAlwaysVisible); + mAlwaysVisible, + mCrossProfileContentSharingStrategy); } } // end Builder @@ -1271,8 +1433,10 @@ public final class UserProperties implements Parcelable { boolean mediaSharedWithParent, boolean credentialShareableWithParent, boolean authAlwaysRequiredToDisableQuietMode, + boolean allowStoppingUserWithDelayedLocking, boolean deleteAppWithParent, - boolean alwaysVisible) { + boolean alwaysVisible, + @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) { mDefaultProperties = null; setShowInLauncher(showInLauncher); setStartWithParent(startWithParent); @@ -1288,7 +1452,9 @@ public final class UserProperties implements Parcelable { setCredentialShareableWithParent(credentialShareableWithParent); setAuthAlwaysRequiredToDisableQuietMode( authAlwaysRequiredToDisableQuietMode); + setAllowStoppingUserWithDelayedLocking(allowStoppingUserWithDelayedLocking); setDeleteAppWithParent(deleteAppWithParent); setAlwaysVisible(alwaysVisible); + setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy); } } diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 1b90570e4609..b04b7badbae0 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -108,3 +108,10 @@ flag { description: "Feature flag to reduce app crashes caused by split installs with INSTALL_DONT_KILL" bug: "291212866" } + +flag { + name: "fix_duplicated_flags" + namespace: "package_manager_service" + description: "Feature flag to fix duplicated PackageManager flag values" + bug: "314815969" +} diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index bab84aadc73b..f876eebe64c1 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -26,4 +26,11 @@ flag { name: "new_settings_intents" description: "Enables settings intents to redirect to new settings page" bug: "307587989" +} + +flag { + namespace: "credential_manager" + name: "new_settings_ui" + description: "Enables new settings UI for VIC" + bug: "315209085" }
\ No newline at end of file diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index 39c9400e7064..4322bedafdeb 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -31,6 +31,7 @@ import android.os.UserHandle; import java.util.Arrays; import java.util.Collection; +import java.util.concurrent.Executor; /** * Receives call backs for changes to content. @@ -54,6 +55,7 @@ public abstract class ContentObserver { private Transport mTransport; // guarded by mLock Handler mHandler; + private final Executor mExecutor; /** * Creates a content observer. @@ -62,6 +64,18 @@ public abstract class ContentObserver { */ public ContentObserver(Handler handler) { mHandler = handler; + mExecutor = null; + } + + /** + * @hide + * Creates a content observer with an executor. + * + * @param executor The executor to run {@link #onChange} on, or null if none. + * @param unused a second argument to avoid source incompatibility. + */ + public ContentObserver(@Nullable Executor executor, int unused) { + mExecutor = executor; } /** @@ -306,12 +320,19 @@ public abstract class ContentObserver { /** @hide */ public final void dispatchChange(boolean selfChange, @NonNull Collection<Uri> uris, @NotifyFlags int flags, @UserIdInt int userId) { - if (mHandler == null) { - onChange(selfChange, uris, flags, userId); - } else { + if (mExecutor != null) { + mExecutor.execute(() -> { + onChange(selfChange, uris, flags, userId); + }); + } else if (mHandler != null) { + // Supporting Handler directly rather than wrapping in a HandlerExecutor + // avoids introducing a RejectedExecutionException for legacy code when + // the post fails. mHandler.post(() -> { onChange(selfChange, uris, flags, userId); }); + } else { + onChange(selfChange, uris, flags, userId); } } diff --git a/core/java/android/database/ExecutorContentObserver.java b/core/java/android/database/ExecutorContentObserver.java new file mode 100644 index 000000000000..3ea807dc305d --- /dev/null +++ b/core/java/android/database/ExecutorContentObserver.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.database; + +import android.annotation.Nullable; + +import java.util.concurrent.Executor; + +/** + * @hide + * + * Receives callbacks for changes to content. + * Must be implemented by objects which are added to a {@link ContentObservable}. + */ +public abstract class ExecutorContentObserver extends ContentObserver { + /** + * Creates a content observer that uses an executor for change handling. + * + * @param executor The executor to run {@link #onChange} on, or null if none. + */ + public ExecutorContentObserver(@Nullable Executor executor) { + super(executor, 0); + } +} diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index bb8924c3919a..e3a552023e4b 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -35,6 +35,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.camera.flags.Flags; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; @@ -206,6 +207,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri private List<CameraCharacteristics.Key<?>> mKeysNeedingPermission; private List<CaptureRequest.Key<?>> mAvailableRequestKeys; private List<CaptureRequest.Key<?>> mAvailableSessionKeys; + private List<CameraCharacteristics.Key<?>> mAvailableSessionCharacteristicsKeys; private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys; private List<CaptureResult.Key<?>> mAvailableResultKeys; private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations; @@ -546,6 +548,27 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } /** + * <p>Get the keys in Camera Characteristics whose values are capture session specific. + * The session specific characteristics can be acquired by calling + * CameraDevice.getSessionCharacteristics(). </p> + * + * <p>Note that getAvailableSessionKeys returns the CaptureRequest keys that are difficult to + * apply per-frame, whereas this function returns CameraCharacteristics keys that are dependent + * on a particular SessionConfiguration.</p> + * + * @return List of CameraCharacteristic keys containing characterisitics specific to a session + * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE. + */ + @NonNull + @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY) + public List<CameraCharacteristics.Key<?>> getAvailableSessionCharacteristicsKeys() { + if (mAvailableSessionCharacteristicsKeys == null) { + mAvailableSessionCharacteristicsKeys = Arrays.asList(CONTROL_ZOOM_RATIO_RANGE); + } + return mAvailableSessionCharacteristicsKeys; + } + + /** * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can * be overridden for physical devices backing a logical multi-camera.</p> * @@ -1332,6 +1355,27 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Boolean>("android.control.autoframingAvailable", boolean.class); /** + * <p>The operating luminance range of low light boost measured in lux (lx).</p> + * <p><b>Range of valid values:</b><br></p> + * <p>The lower bound indicates the lowest scene luminance value the AE mode + * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' can operate within. Scenes of lower luminance + * than this may receive less brightening, increased noise, or artifacts.</p> + * <p>The upper bound indicates the luminance threshold at the point when the mode is enabled. + * For example, 'Range[0.3, 30.0]' defines 0.3 lux being the lowest scene luminance the + * mode can reliably support. 30.0 lux represents the threshold when this mode is + * activated. Scenes measured at less than or equal to 30 lux will activate low light + * boost.</p> + * <p>If this key is defined, then the AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' will + * also be present.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST) + public static final Key<android.util.Range<Float>> CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE = + new Key<android.util.Range<Float>>("android.control.lowLightBoostInfoLuminanceRange", new TypeReference<android.util.Range<Float>>() {{ }}); + + /** * <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera * device.</p> * <p>Full-capability camera devices must always support OFF; camera devices that support diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 58cba414fc47..3835c5201946 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -16,6 +16,7 @@ package android.hardware.camera2; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,6 +29,8 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.os.Handler; import android.view.Surface; +import com.android.internal.camera.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; @@ -1412,7 +1415,35 @@ public abstract class CameraDevice implements AutoCloseable { throw new UnsupportedOperationException("Subclasses must override this method"); } - /** + /** + * <p>Get camera characteristics for a particular session configuration by the camera device.</p> + * + * <p>The camera characteristics returned here is typically more limited than the characteristics + * returned from {@link CameraManager#getCameraCharacteristics}. The keys that have more limited + * values are listed in + * {@link CameraCharacteristics#getAvailableSessionCharacteristicsKeys}. </p> + * + * <p>Other than that, the characteristics returned here can be used in the same way as those + * returned from {@link CameraManager#getCameraCharacteristics}.</p> + * + * @param sessionConfig : The session configuration for which characteristics are fetched. + * @return CameraCharacteristics specific to a given session configuration. + * @throws UnsupportedOperationException if the query operation is not supported by the camera + * device + * @throws IllegalArgumentException if the session configuration is invalid + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if the camera device has been closed + * @see android.hardware.camera2.CameraCharacteristics#getAvailableSessionCharacteristicsKeys + */ + @NonNull + @FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY) + public CameraCharacteristics getSessionCharacteristics( + @NonNull SessionConfiguration sessionConfig) throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** * A callback objects for receiving updates about the state of a camera device. * * <p>A callback instance must be provided to the {@link CameraManager#openCamera} method to diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index d4d1ab373157..8196bf505e02 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -1058,6 +1058,12 @@ public final class CameraExtensionCharacteristics { * <p>The set returned is not modifiable, so any attempts to modify it will throw * a {@code UnsupportedOperationException}.</p> * + * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} + * or newer versions are required to support {@link CaptureRequest#CONTROL_AF_MODE}, + * {@link CaptureRequest#CONTROL_AF_REGIONS}, {@link CaptureRequest#CONTROL_AF_TRIGGER}, + * {@link CaptureRequest#CONTROL_ZOOM_RATIO} for + * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p> + * * @param extension the extension type * * @return non-modifiable set of capture keys supported by camera extension session initialized @@ -1139,6 +1145,12 @@ public final class CameraExtensionCharacteristics { * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable} * callback will not be fired.</p> * + * <p>Devices launching on Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} + * or newer versions are required to support {@link CaptureResult#CONTROL_AF_MODE}, + * {@link CaptureResult#CONTROL_AF_REGIONS}, {@link CaptureResult#CONTROL_AF_TRIGGER}, + * {@link CaptureResult#CONTROL_AF_STATE}, {@link CaptureResult#CONTROL_ZOOM_RATIO} for + * {@link CameraExtensionCharacteristics#EXTENSION_NIGHT}.</p> + * * @param extension the extension type * * @return non-modifiable set of capture result keys supported by camera extension session diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 002c0b207506..bcce4b65be18 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -1844,7 +1844,7 @@ public final class CameraManager { * Remaps Camera Ids in the CameraService. * * @hide - */ + */ @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA) public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping) throws CameraAccessException, SecurityException, IllegalArgumentException { @@ -1852,6 +1852,29 @@ public final class CameraManager { } /** + * Injects session params into existing clients in the CameraService. + * + * @param cameraId The camera id of client to inject session params into. + * If no such client exists for cameraId, no injection will + * take place. + * @param sessionParams A {@link CaptureRequest} object containing the + * the sessionParams to inject into the existing client. + * + * @throws CameraAccessException {@link CameraAccessException#CAMERA_DISCONNECTED} will be + * thrown if camera service is not available. Further, if + * if no such client exists for cameraId, + * {@link CameraAccessException#CAMERA_ERROR} will be thrown. + * @throws SecurityException If the caller does not have permission to inject session + * params + * @hide + */ + @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA) + public void injectSessionParams(@NonNull String cameraId, @NonNull CaptureRequest sessionParams) + throws CameraAccessException, SecurityException { + CameraManagerGlobal.get().injectSessionParams(cameraId, sessionParams); + } + + /** * Reports {@link CameraExtensionSessionStats} to the {@link ICameraService} to be logged for * currently active session. Validation is done downstream. * @@ -2110,6 +2133,30 @@ public final class CameraManager { } } + /** Injects session params into an existing client for cameraid. */ + public void injectSessionParams(@NonNull String cameraId, + @NonNull CaptureRequest sessionParams) + throws CameraAccessException, SecurityException { + synchronized (mLock) { + ICameraService cameraService = getCameraService(); + if (cameraService == null) { + throw new CameraAccessException( + CameraAccessException.CAMERA_DISCONNECTED, + "Camera service is currently unavailable."); + } + + try { + cameraService.injectSessionParams(cameraId, sessionParams.getNativeMetadata()); + } catch (ServiceSpecificException e) { + throwAsPublicException(e); + } catch (RemoteException e) { + throw new CameraAccessException( + CameraAccessException.CAMERA_DISCONNECTED, + "Camera service is currently unavailable."); + } + } + } + private String[] extractCameraIdListLocked() { String[] cameraIds = null; int idCount = 0; diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 003718e6b54e..765a8f7c842c 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2333,6 +2333,46 @@ public abstract class CameraMetadata<TKey> { */ public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5; + /** + * <p>Like 'ON' but applies additional brightness boost in low light scenes.</p> + * <p>When the scene lighting conditions are within the range defined by + * {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange} this mode will apply additional + * brightness boost.</p> + * <p>This mode will automatically adjust the intensity of low light boost applied + * according to the scene lighting conditions. A darker scene will receive more boost + * while a brighter scene will receive less boost.</p> + * <p>This mode can ignore the set target frame rate to allow more light to be captured + * which can result in choppier motion. The frame rate can extend to lower than the + * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges} but will not go below 10 FPS. This mode + * can also increase the sensor sensitivity gain which can result in increased luma + * and chroma noise. The sensor sensitivity gain can extend to higher values beyond + * {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}. This mode may also apply additional + * processing to recover details in dark and bright areas of the image,and noise + * reduction at high sensitivity gain settings to manage the trade-off between light + * sensitivity and capture noise.</p> + * <p>This mode is restricted to two output surfaces. One output surface type can either + * be SurfaceView or TextureView. Another output surface type can either be MediaCodec + * or MediaRecorder. This mode cannot be used with a target FPS range higher than 30 + * FPS.</p> + * <p>If the session configuration is not supported, the AE mode reported in the + * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p> + * <p>The application can observe the CapturerResult field + * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or + * 'INACTIVE'.</p> + * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the + * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}. + * This mode will be 'INACTIVE' once the scene lighting condition is greater than the + * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.</p> + * + * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES + * @see CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE + * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE + * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE + * @see CaptureRequest#CONTROL_AE_MODE + */ + @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST) + public static final int CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY = 6; + // // Enumeration values for CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER // @@ -4074,6 +4114,24 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AUTOFRAMING_STATE_CONVERGED = 2; // + // Enumeration values for CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE + // + + /** + * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled but not applied.</p> + * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE + */ + @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST) + public static final int CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE = 0; + + /** + * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled and applied.</p> + * @see CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE + */ + @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST) + public static final int CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE = 1; + + // // Enumeration values for CaptureResult#FLASH_STATE // diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 35f295a36d87..ab4406c37c8e 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2814,6 +2814,31 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.autoframingState", int.class); /** + * <p>Current state of the low light boost AE mode.</p> + * <p>When low light boost is enabled by setting the AE mode to + * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light + * boost when the light level threshold is exceeded.</p> + * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can + * indicate when it is not being applied by returning 'INACTIVE'.</p> + * <p>This key will be absent from the CaptureResult if AE mode is not set to + * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li> + * <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE ACTIVE}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE + * @see #CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_CAMERA_AE_MODE_LOW_LIGHT_BOOST) + public static final Key<Integer> CONTROL_LOW_LIGHT_BOOST_STATE = + new Key<Integer>("android.control.lowLightBoostState", int.class); + + /** * <p>Operation mode for edge * enhancement.</p> * <p>Edge enhancement improves sharpness and details in the captured image. OFF means diff --git a/core/java/android/hardware/camera2/extension/RequestProcessor.java b/core/java/android/hardware/camera2/extension/RequestProcessor.java index 7c099d67e6e9..bf5ea12df358 100644 --- a/core/java/android/hardware/camera2/extension/RequestProcessor.java +++ b/core/java/android/hardware/camera2/extension/RequestProcessor.java @@ -250,7 +250,7 @@ public final class RequestProcessor { */ @FlaggedApi(Flags.FLAG_CONCERT_MODE) @NonNull - List<Pair<CaptureRequest.Key, Object>> getParameters() { + public List<Pair<CaptureRequest.Key, Object>> getParameters() { return mParameters; } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 3851e368fb62..ccb24e7d2457 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -786,6 +786,18 @@ public class CameraDeviceImpl extends CameraDevice } } + @Override + public CameraCharacteristics getSessionCharacteristics( + @NonNull SessionConfiguration sessionConfig) throws CameraAccessException, + UnsupportedOperationException, IllegalArgumentException { + synchronized (mInterfaceLock) { + checkIfCameraClosedOrInError(); + CameraMetadataNative info = mRemoteDevice.getSessionCharacteristics(sessionConfig); + + return new CameraCharacteristics(info); + } + } + /** * For use by backwards-compatibility code only. */ diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index b6b1968bfcdd..2129260b0ae8 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -16,19 +16,12 @@ package android.hardware.camera2.impl; -import static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED; -import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED; -import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; -import static android.hardware.camera2.CameraAccessException.CAMERA_ERROR; -import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE; -import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL; - import android.hardware.ICameraService; -import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.ICameraDeviceCallbacks; +import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.ICameraOfflineSession; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.OutputConfiguration; @@ -205,6 +198,28 @@ public class ICameraDeviceUserWrapper { } } + /** + * Fetches the CameraCharacteristics for a given session configuration. + */ + public CameraMetadataNative getSessionCharacteristics(SessionConfiguration sessionConfig) + throws CameraAccessException { + try { + return mRemoteDevice.getSessionCharacteristics(sessionConfig); + } catch (ServiceSpecificException e) { + if (e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { + throw new UnsupportedOperationException("Session characteristics query not " + + "supported"); + } else if (e.errorCode == ICameraService.ERROR_ILLEGAL_ARGUMENT) { + throw new IllegalArgumentException("Invalid session configuration"); + } + + throw e; + } catch (Throwable t) { + CameraManager.throwAsPublicException(t); + throw new UnsupportedOperationException("Unexpected exception", t); + } + } + public long flush() throws CameraAccessException { try { return mRemoteDevice.flush(); diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 6a83cee10309..05a1abeaf479 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -33,6 +33,7 @@ import java.util.ArrayList; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public abstract class BatteryConsumer { private static final String TAG = "BatteryConsumer"; diff --git a/core/java/android/os/CoolingDevice.java b/core/java/android/os/CoolingDevice.java index 4ddcd9d4ff8b..06ec72051c2f 100644 --- a/core/java/android/os/CoolingDevice.java +++ b/core/java/android/os/CoolingDevice.java @@ -55,7 +55,11 @@ public final class CoolingDevice implements Parcelable { TYPE_TPU, TYPE_POWER_AMPLIFIER, TYPE_DISPLAY, - TYPE_SPEAKER + TYPE_SPEAKER, + TYPE_WIFI, + TYPE_CAMERA, + TYPE_FLASHLIGHT, + TYPE_USB_PORT }) @Retention(RetentionPolicy.SOURCE) public @interface Type {} @@ -84,6 +88,14 @@ public final class CoolingDevice implements Parcelable { public static final int TYPE_DISPLAY = CoolingType.DISPLAY; /** Speaker cooling device */ public static final int TYPE_SPEAKER = CoolingType.SPEAKER; + /** WiFi cooling device */ + public static final int TYPE_WIFI = CoolingType.WIFI; + /** Camera cooling device */ + public static final int TYPE_CAMERA = CoolingType.CAMERA; + /** Flashlight cooling device */ + public static final int TYPE_FLASHLIGHT = CoolingType.FLASHLIGHT; + /** USB PORT cooling device */ + public static final int TYPE_USB_PORT = CoolingType.USB_PORT; /** * Verify a valid cooling device type. @@ -91,7 +103,7 @@ public final class CoolingDevice implements Parcelable { * @return true if a cooling device type is valid otherwise false. */ public static boolean isValidType(@Type int type) { - return type >= TYPE_FAN && type <= TYPE_SPEAKER; + return type >= TYPE_FAN && type <= TYPE_USB_PORT; } public CoolingDevice(long value, @Type int type, @NonNull String name) { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index c527cb5344c1..04d6f61d84c9 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -2702,4 +2702,13 @@ public final class Debug * @hide */ public static native boolean isVmapStack(); + + /** + * Log internal statistics about the allocator. + * @return true if the statistics were logged properly, false if not. + * + * @hide + */ + public static native boolean logAllocatorStats(); + } diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl index 0f2756914fa6..65e498e14475 100644 --- a/core/java/android/os/IVibratorManagerService.aidl +++ b/core/java/android/os/IVibratorManagerService.aidl @@ -41,5 +41,5 @@ interface IVibratorManagerService { // There is no order guarantee with respect to the two-way APIs above like // vibrate/isVibrating/cancel. oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant, - boolean always, String reason, IBinder token); + boolean always, String reason); } diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java index bc85412e851b..8e8392302824 100644 --- a/core/java/android/os/SystemVibratorManager.java +++ b/core/java/android/os/SystemVibratorManager.java @@ -39,6 +39,7 @@ public class SystemVibratorManager extends VibratorManager { private final IVibratorManagerService mService; private final Context mContext; + private final int mUid; private final Binder mToken = new Binder(); private final Object mLock = new Object(); @GuardedBy("mLock") @@ -56,6 +57,7 @@ public class SystemVibratorManager extends VibratorManager { public SystemVibratorManager(Context context) { super(context); mContext = context; + mUid = Process.myUid(); mService = IVibratorManagerService.Stub.asInterface( ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE)); } @@ -152,8 +154,7 @@ public class SystemVibratorManager extends VibratorManager { } try { mService.performHapticFeedback( - Process.myUid(), mContext.getDeviceId(), mPackageName, constant, always, reason, - mToken); + mUid, mContext.getDeviceId(), mPackageName, constant, always, reason); } catch (RemoteException e) { Log.w(TAG, "Failed to perform haptic feedback.", e); } diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java index a138431f745a..2e12278a0ad3 100644 --- a/core/java/android/os/Temperature.java +++ b/core/java/android/os/Temperature.java @@ -79,7 +79,13 @@ public final class Temperature implements Parcelable { TYPE_TPU, TYPE_DISPLAY, TYPE_MODEM, - TYPE_SOC + TYPE_SOC, + TYPE_WIFI, + TYPE_CAMERA, + TYPE_FLASHLIGHT, + TYPE_SPEAKER, + TYPE_AMBIENT, + TYPE_POGO }) @Retention(RetentionPolicy.SOURCE) public @interface Type {} @@ -101,6 +107,12 @@ public final class Temperature implements Parcelable { public static final int TYPE_DISPLAY = TemperatureType.DISPLAY; public static final int TYPE_MODEM = TemperatureType.MODEM; public static final int TYPE_SOC = TemperatureType.SOC; + public static final int TYPE_WIFI = TemperatureType.WIFI; + public static final int TYPE_CAMERA = TemperatureType.CAMERA; + public static final int TYPE_FLASHLIGHT = TemperatureType.FLASHLIGHT; + public static final int TYPE_SPEAKER = TemperatureType.SPEAKER; + public static final int TYPE_AMBIENT = TemperatureType.AMBIENT; + public static final int TYPE_POGO = TemperatureType.POGO; /** * Verify a valid Temperature type. @@ -108,7 +120,7 @@ public final class Temperature implements Parcelable { * @return true if a Temperature type is valid otherwise false. */ public static boolean isValidType(@Type int type) { - return type >= TYPE_UNKNOWN && type <= TYPE_SOC; + return type >= TYPE_UNKNOWN && type <= TYPE_POGO; } /** diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 980c13c5c2d6..145981c92283 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -76,3 +76,11 @@ flag { description: "Guards the ADPF GPU APIs." bug: "284324521" } + +flag { + name: "battery_service_support_current_adb_command" + namespace: "backstage_power" + description: "Whether or not BatteryService supports adb commands for Current values." + is_fixed_read_only: true + bug: "315037695" +} diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index 437668c9a7de..69d86a6604ad 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -44,10 +44,3 @@ flag { description: "Enables the independent keyboard vibration settings feature" bug: "289107579" } - -flag { - namespace: "haptics" - name: "adaptive_haptics_enabled" - description: "Enables the adaptive haptics feature" - bug: "305961689" -} diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 9fe2599dd39b..5a12760f4e3f 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -318,7 +318,7 @@ public abstract class PermissionControllerService extends Service { * a virtual device. See {@link Context#DEVICE_ID_DEFAULT} */ @BinderThread - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public void onOneTimePermissionSessionTimeout(@NonNull String packageName, int deviceId) { onOneTimePermissionSessionTimeout(packageName); @@ -393,7 +393,7 @@ public abstract class PermissionControllerService extends Service { * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection) */ @BinderThread - @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) + @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) public void onRevokeSelfPermissionsOnKill(@NonNull String packageName, @NonNull List<String> permissions, int deviceId, @NonNull Runnable callback) { onRevokeSelfPermissionsOnKill(packageName, permissions, callback); diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index cbeb82175bfd..d09c2290b38a 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -1,7 +1,7 @@ package: "android.permission.flags" flag { - name: "device_aware_permission_apis" + name: "device_aware_permission_apis_enabled" is_fixed_read_only: true namespace: "permissions" description: "enable device aware permission APIs" diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8b5995a740f8..9c27f19b349a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -608,6 +608,23 @@ public final class Settings { /** * Activity Action: Show settings to allow configuration of + * {@link Manifest.permission#MEDIA_ROUTING_CONTROL} permission. + * + * Input: Optionally, the Intent's data URI can specify the application package name to + * directly invoke the management GUI specific to the package name. For example + * "package:com.my.app". However, modifying this permission setting for any package is allowed + * only when that package holds an appropriate companion device profile such as + * {@link android.companion.AssociationRequest#DEVICE_PROFILE_WATCH}. + * <p> + * Output: Nothing. + */ + @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = + "android.settings.REQUEST_MEDIA_ROUTING_CONTROL"; + + /** + * Activity Action: Show settings to allow configuration of * {@link Manifest.permission#RUN_USER_INITIATED_JOBS} permission * * Input: Optionally, the Intent's data URI can specify the application package name to diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 0dc04133e664..28ef70b14026 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -8,6 +8,13 @@ flag { } flag { + name: "mgf1_digest_setter" + namespace: "hardware_backed_security" + description: "Feature flag for mgf1 digest setter in key generation and import parameters." + bug: "308378912" +} + +flag { name: "fix_unlocked_device_required_keys_v2" namespace: "hardware_backed_security" description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys" diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig new file mode 100644 index 000000000000..91a713ee6250 --- /dev/null +++ b/core/java/android/service/dreams/flags.aconfig @@ -0,0 +1,9 @@ +package: "android.service.dreams" + +flag { + name: "dream_overlay_host" + namespace: "communal" + description: "This flag enables using a host to handle displaying a dream's overlay rather than " + "relying on the dream's window" + bug: "291990564" +} diff --git a/core/java/android/service/notification/DeviceEffectsApplier.java b/core/java/android/service/notification/DeviceEffectsApplier.java index 234ff4dd0852..5194cdd47933 100644 --- a/core/java/android/service/notification/DeviceEffectsApplier.java +++ b/core/java/android/service/notification/DeviceEffectsApplier.java @@ -16,6 +16,8 @@ package android.service.notification; +import android.service.notification.ZenModeConfig.ConfigChangeOrigin; + /** * Responsible for making any service calls needed to apply the set of {@link ZenDeviceEffects} that * make sense for the current platform. @@ -33,6 +35,13 @@ public interface DeviceEffectsApplier { * * <p>This will be called whenever the set of consolidated effects changes (normally through * the activation or deactivation of zen rules). + * + * @param effects The effects that should be active and inactive. + * @param source The origin of the change. Because the application of specific effects can be + * disruptive (e.g. lead to Activity recreation), that operation can in some + * cases be deferred (e.g. until screen off). However, if the effects are + * changing as a result of an explicit user action, then it makes sense to + * apply them immediately regardless. */ - void apply(ZenDeviceEffects effects); + void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int source); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index f6128ea80c3b..a5b087c05dfa 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -26,6 +26,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AlarmManager; @@ -61,6 +62,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -79,7 +82,66 @@ import java.util.UUID; * @hide */ public class ZenModeConfig implements Parcelable { - private static String TAG = "ZenModeConfig"; + private static final String TAG = "ZenModeConfig"; + + /** + * The {@link ZenModeConfig} is being updated because of an unknown reason. + */ + public static final int UPDATE_ORIGIN_UNKNOWN = 0; + + /** + * The {@link ZenModeConfig} is being updated because of system initialization (i.e. load from + * storage, on device boot). + */ + public static final int UPDATE_ORIGIN_INIT = 1; + + /** The {@link ZenModeConfig} is being updated (replaced) because of a user switch or unlock. */ + public static final int UPDATE_ORIGIN_INIT_USER = 2; + + /** The {@link ZenModeConfig} is being updated because of a user action, for example: + * <ul> + * <li>{@link NotificationManager#setAutomaticZenRuleState} with a + * {@link Condition#source} equal to {@link Condition#SOURCE_USER_ACTION}.</li> + * <li>Adding, updating, or removing a rule from Settings.</li> + * <li>Directly activating or deactivating/snoozing a rule through some UI affordance (e.g. + * Quick Settings).</li> + * </ul> + */ + public static final int UPDATE_ORIGIN_USER = 3; + + /** + * The {@link ZenModeConfig} is being "independently" updated by an app, and not as a result of + * a user's action inside that app (for example, activating an {@link AutomaticZenRule} based on + * a previously set schedule). + */ + public static final int UPDATE_ORIGIN_APP = 4; + + /** + * The {@link ZenModeConfig} is being updated by the System or SystemUI. Note that this only + * includes cases where the call is coming from the System/SystemUI but the change is not due to + * a user action (e.g. automatically activating a schedule-based rule). If the change is a + * result of a user action (e.g. activating a rule by tapping on its QS tile) then + * {@link #UPDATE_ORIGIN_USER} is used instead. + */ + public static final int UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI = 5; + + /** + * The {@link ZenModeConfig} is being updated (replaced) because the user's DND configuration + * is being restored from a backup. + */ + public static final int UPDATE_ORIGIN_RESTORE_BACKUP = 6; + + @IntDef(prefix = { "UPDATE_ORIGIN_" }, value = { + UPDATE_ORIGIN_UNKNOWN, + UPDATE_ORIGIN_INIT, + UPDATE_ORIGIN_INIT_USER, + UPDATE_ORIGIN_USER, + UPDATE_ORIGIN_APP, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + UPDATE_ORIGIN_RESTORE_BACKUP + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ConfigChangeOrigin {} public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY; public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS; diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java index d184b1eb96ab..76b076be8fab 100644 --- a/core/java/android/service/voice/VisualQueryDetectionService.java +++ b/core/java/android/service/voice/VisualQueryDetectionService.java @@ -338,16 +338,24 @@ public abstract class VisualQueryDetectionService extends Service /** * Overrides {@link Context#openFileInput} to read files with the given file names under the - * internal app storage of the {@link VoiceInteractionService}, i.e., only files stored in - * {@link Context#getFilesDir()} can be opened. + * internal app storage of the {@link VoiceInteractionService}, i.e., the input file path would + * be added with {@link Context#getFilesDir()} as prefix. + * + * @param filename Relative path of a file under {@link Context#getFilesDir()}. + * @throws FileNotFoundException if the file does not exist or cannot be open. */ @Override - public @Nullable FileInputStream openFileInput(@NonNull String filename) throws + public @NonNull FileInputStream openFileInput(@NonNull String filename) throws FileNotFoundException { try { AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>(); + assert mDetectorSessionStorageService != null; mDetectorSessionStorageService.openFile(filename, future); ParcelFileDescriptor pfd = future.get(); + if (pfd == null) { + throw new FileNotFoundException( + "File does not exist. Unable to open " + filename + "."); + } return new FileInputStream(pfd.getFileDescriptor()); } catch (RemoteException | ExecutionException | InterruptedException e) { Log.w(TAG, "Cannot open file due to remote service failure"); diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java index 91de894c1d93..b7d97057a08b 100644 --- a/core/java/android/service/voice/VisualQueryDetector.java +++ b/core/java/android/service/voice/VisualQueryDetector.java @@ -447,12 +447,12 @@ public class VisualQueryDetector { public void onOpenFile(String filename, AndroidFuture future) throws RemoteException { Slog.v(TAG, "BinderCallback#onOpenFile " + filename); Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> { - Slog.v(TAG, "onOpenFile: " + filename); + Slog.v(TAG, "onOpenFile: " + filename + "under internal app storage."); File f = new File(mContext.getFilesDir(), filename); ParcelFileDescriptor pfd = null; try { - Slog.d(TAG, "opened a file with ParcelFileDescriptor."); pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); + Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor."); } catch (FileNotFoundException e) { Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned."); } finally { diff --git a/core/java/android/util/DayOfMonthCursor.java b/core/java/android/util/DayOfMonthCursor.java index 393b98e324d3..ac5dc3a169c8 100644 --- a/core/java/android/util/DayOfMonthCursor.java +++ b/core/java/android/util/DayOfMonthCursor.java @@ -32,6 +32,7 @@ package android.util; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class DayOfMonthCursor extends MonthDisplayHelper { private int mRow; diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java index fef5acd42e4e..24640c974500 100644 --- a/core/java/android/util/DumpableContainer.java +++ b/core/java/android/util/DumpableContainer.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; /** * Represents a container that manages {@link Dumpable dumpables}. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public interface DumpableContainer { /** diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java index fbc66e6bda2a..783b2d63bcf3 100644 --- a/core/java/android/util/KeyValueListParser.java +++ b/core/java/android/util/KeyValueListParser.java @@ -28,6 +28,7 @@ import java.time.format.DateTimeParseException; * can be used. * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class KeyValueListParser { private final ArrayMap<String, String> mValues = new ArrayMap<>(); private final TextUtils.StringSplitter mSplitter; diff --git a/core/java/android/util/LongArrayQueue.java b/core/java/android/util/LongArrayQueue.java index 354f8df0a43d..b9752fe4d1cc 100644 --- a/core/java/android/util/LongArrayQueue.java +++ b/core/java/android/util/LongArrayQueue.java @@ -28,6 +28,7 @@ import java.util.NoSuchElementException; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class LongArrayQueue { private long[] mValues; diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index d4a012602f80..86268facbec0 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -46,6 +46,7 @@ import com.android.internal.util.Preconditions; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class LongSparseLongArray implements Cloneable { @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public. private long[] mKeys; diff --git a/core/java/android/util/MonthDisplayHelper.java b/core/java/android/util/MonthDisplayHelper.java index c3f13fc2176b..3bd292b4ba49 100644 --- a/core/java/android/util/MonthDisplayHelper.java +++ b/core/java/android/util/MonthDisplayHelper.java @@ -24,6 +24,7 @@ import java.util.Calendar; * * Not thread safe. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class MonthDisplayHelper { // display pref diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java index 39d1f2cbef19..dc470d7edc53 100644 --- a/core/java/android/util/RecurrenceRule.java +++ b/core/java/android/util/RecurrenceRule.java @@ -42,6 +42,7 @@ import java.util.Objects; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class RecurrenceRule implements Parcelable { private static final String TAG = "RecurrenceRule"; private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java index f20767b7485a..07ab996af12f 100644 --- a/core/java/android/util/RotationUtils.java +++ b/core/java/android/util/RotationUtils.java @@ -36,6 +36,7 @@ import android.view.SurfaceControl; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class RotationUtils { /** diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java index 4b0cbe45d857..8a0b11e09d89 100644 --- a/core/java/android/util/SparseDoubleArray.java +++ b/core/java/android/util/SparseDoubleArray.java @@ -41,6 +41,7 @@ package android.util; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class SparseDoubleArray implements Cloneable { /** * The int->double map, but storing the doubles as longs using diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index 61f29a40ff50..80f6a4a0847f 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class SparseSetArray<T> { private final SparseArray<ArraySet<T>> mData; diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java index 4bbc0f863603..16d6082649a7 100644 --- a/core/java/android/util/StateSet.java +++ b/core/java/android/util/StateSet.java @@ -34,7 +34,6 @@ import com.android.internal.R; * and not have static methods here but there is some concern about * performance since these methods are called during view drawing. */ - public class StateSet { /** * The order here is very important to diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java index 2a33caaf7e28..ec6e90b4153e 100644 --- a/core/java/android/util/Xml.java +++ b/core/java/android/util/Xml.java @@ -334,6 +334,7 @@ public class Xml { * * @hide */ + @android.ravenwood.annotation.RavenwoodKeep public static void copy(@NonNull XmlPullParser in, @NonNull XmlSerializer out) throws XmlPullParserException, IOException { // Some parsers may have already consumed the event that starts the @@ -393,6 +394,7 @@ public class Xml { * unsupported, which can confuse serializers. This method normalizes empty * strings to be {@code null}. */ + @android.ravenwood.annotation.RavenwoodKeep private static @Nullable String normalizeNamespace(@Nullable String namespace) { if (namespace == null || namespace.isEmpty()) { return null; diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index fd5517d29d74..f28574ecb3b2 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -27,9 +27,6 @@ import android.window.SurfaceSyncGroup; import com.android.window.flags.Flags; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - /** * Provides an interface to the root-Surface of a View Hierarchy or Window. This * is used in combination with the {@link android.view.SurfaceControl} API to enable @@ -197,42 +194,6 @@ public interface AttachedSurfaceControl { } /** - * Add a trusted presentation listener on the SurfaceControl associated with this window. - * - * @param t Transaction that the trusted presentation listener is added on. This should - * be applied by the caller. - * @param thresholds The {@link SurfaceControl.TrustedPresentationThresholds} that will specify - * when the to invoke the callback. - * @param executor The {@link Executor} where the callback will be invoked on. - * @param listener The {@link Consumer} that will receive the callbacks when entered or - * exited the threshold. - * - * @see SurfaceControl.Transaction#setTrustedPresentationCallback(SurfaceControl, - * SurfaceControl.TrustedPresentationThresholds, Executor, Consumer) - * - * @hide b/287076178 un-hide with API bump - */ - default void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, - @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, - @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { - } - - /** - * Remove a trusted presentation listener on the SurfaceControl associated with this window. - * - * @param t Transaction that the trusted presentation listener removed on. This should - * be applied by the caller. - * @param listener The {@link Consumer} that was previously registered with - * addTrustedPresentationCallback that should be removed. - * - * @see SurfaceControl.Transaction#clearTrustedPresentationCallback(SurfaceControl) - * @hide b/287076178 un-hide with API bump - */ - default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, - @NonNull Consumer<Boolean> listener) { - } - - /** * Transfer the currently in progress touch gesture from the host to the requested * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the * SurfaceControlViewHost was created with the current host's inputToken. diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 17bbee6d020f..36b74e39072a 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -73,6 +73,8 @@ import android.window.ISurfaceSyncGroupCompletedListener; import android.window.ITaskFpsCallback; import android.window.ScreenCapture; import android.window.WindowContextInfo; +import android.window.ITrustedPresentationListener; +import android.window.TrustedPresentationThresholds; /** * System private interface to the window manager. @@ -1075,4 +1077,10 @@ interface IWindowManager @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MONITOR_INPUT)") void unregisterDecorViewGestureListener(IDecorViewGestureListener listener, int displayId); + + void registerTrustedPresentationListener(in IBinder window, in ITrustedPresentationListener listener, + in TrustedPresentationThresholds thresholds, int id); + + + void unregisterTrustedPresentationListener(in ITrustedPresentationListener listener, int id); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1530aa78d73d..5cbb42e0e346 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -826,11 +826,19 @@ public final class ViewRootImpl implements ViewParent, * The resolved pointer icon type requested by this window. * A null value indicates the resolved pointer icon has not yet been calculated. */ + // TODO(b/293587049): Remove pointer icon tracking by type when refactor is complete. @Nullable private Integer mPointerIconType = null; private PointerIcon mCustomPointerIcon = null; /** + * The resolved pointer icon requested by this window. + * A null value indicates the resolved pointer icon has not yet been calculated. + */ + @Nullable + private PointerIcon mResolvedPointerIcon = null; + + /** * see {@link #playSoundEffect(int)} */ AudioManager mAudioManager; @@ -3216,6 +3224,12 @@ public final class ViewRootImpl implements ViewParent, endDragResizing(); destroyHardwareResources(); } + + if (sToolkitSetFrameRateReadOnlyFlagValue && viewVisibility == View.VISIBLE) { + // Boost frame rate when the viewVisibility becomes true. + // This is mainly for lanuchers that lanuch new windows. + boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); + } } // Non-visible windows can't hold accessibility focus. @@ -3925,6 +3939,11 @@ public final class ViewRootImpl implements ViewParent, focused.restoreDefaultFocus(); } } + + if (sToolkitSetFrameRateReadOnlyFlagValue) { + // Boost the frame rate when the ViewRootImpl first becomes available. + boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME); + } } final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible; @@ -7399,12 +7418,14 @@ public final class ViewRootImpl implements ViewParent, // Other apps or the window manager may change the icon type outside of // this app, therefore the icon type has to be reset on enter/exit event. mPointerIconType = null; + mResolvedPointerIcon = null; } if (action != MotionEvent.ACTION_HOVER_EXIT) { // Resolve the pointer icon if (!updatePointerIcon(event) && action == MotionEvent.ACTION_HOVER_MOVE) { mPointerIconType = null; + mResolvedPointerIcon = null; } } @@ -7459,6 +7480,7 @@ public final class ViewRootImpl implements ViewParent, private void resetPointerIcon(MotionEvent event) { mPointerIconType = null; + mResolvedPointerIcon = null; updatePointerIcon(event); } @@ -7496,6 +7518,21 @@ public final class ViewRootImpl implements ViewParent, pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); } + if (enablePointerChoreographer()) { + if (pointerIcon == null) { + pointerIcon = PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_NOT_SPECIFIED); + } + if (Objects.equals(mResolvedPointerIcon, pointerIcon)) { + return true; + } + mResolvedPointerIcon = pointerIcon; + + InputManagerGlobal.getInstance() + .setPointerIcon(pointerIcon, event.getDisplayId(), + event.getDeviceId(), event.getPointerId(0), getInputToken()); + return true; + } + final int pointerType = (pointerIcon != null) ? pointerIcon.getType() : PointerIcon.TYPE_NOT_SPECIFIED; @@ -7503,34 +7540,18 @@ public final class ViewRootImpl implements ViewParent, mPointerIconType = pointerType; mCustomPointerIcon = null; if (mPointerIconType != PointerIcon.TYPE_CUSTOM) { - if (enablePointerChoreographer()) { - InputManagerGlobal - .getInstance() - .setPointerIcon(PointerIcon.getSystemIcon(mContext, pointerType), - event.getDisplayId(), event.getDeviceId(), - event.getPointerId(pointerIndex), getInputToken()); - } else { - InputManagerGlobal - .getInstance() - .setPointerIconType(pointerType); - } + InputManagerGlobal + .getInstance() + .setPointerIconType(pointerType); return true; } } if (mPointerIconType == PointerIcon.TYPE_CUSTOM && !pointerIcon.equals(mCustomPointerIcon)) { mCustomPointerIcon = pointerIcon; - if (enablePointerChoreographer()) { - InputManagerGlobal - .getInstance() - .setPointerIcon(mCustomPointerIcon, - event.getDisplayId(), event.getDeviceId(), - event.getPointerId(pointerIndex), getInputToken()); - } else { - InputManagerGlobal - .getInstance() - .setCustomPointerIcon(mCustomPointerIcon); - } + InputManagerGlobal + .getInstance() + .setCustomPointerIcon(mCustomPointerIcon); } return true; } @@ -12005,18 +12026,6 @@ public final class ViewRootImpl implements ViewParent, scheduleTraversals(); } - @Override - public void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, - @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, - @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { - t.setTrustedPresentationCallback(getSurfaceControl(), thresholds, executor, listener); - } - - @Override - public void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, - @NonNull Consumer<Boolean> listener) { - t.clearTrustedPresentationCallback(getSurfaceControl()); - } private void logAndTrace(String msg) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { @@ -12036,7 +12045,7 @@ public final class ViewRootImpl implements ViewParent, try { if (mLastPreferredFrameRateCategory != frameRateCategory) { mFrameRateTransaction.setFrameRateCategory(mSurfaceControl, - frameRateCategory, false).applyAsyncUnsafe(); + frameRateCategory, false).applyAsyncUnsafe(); mLastPreferredFrameRateCategory = frameRateCategory; } } catch (Exception e) { @@ -12159,6 +12168,22 @@ public final class ViewRootImpl implements ViewParent, return mPreferredFrameRate; } + /** + * Get the value of mIsFrameRateBoosting + */ + @VisibleForTesting + public boolean getIsFrameRateBoosting() { + return mIsFrameRateBoosting; + } + + private void boostFrameRate(int boostTimeOut) { + mIsFrameRateBoosting = true; + setPreferredFrameRateCategory(mPreferredFrameRateCategory); + mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT, + boostTimeOut); + } + @Override public boolean transferHostTouchGestureToEmbedded( @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 046ea77f196d..c7e180732fd4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -122,7 +122,11 @@ import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityNodeInfo; +import android.window.ITrustedPresentationListener; import android.window.TaskFpsCallback; +import android.window.TrustedPresentationThresholds; + +import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -656,26 +660,35 @@ public interface WindowManager extends ViewManager { /** * Display IME Policy: The IME should appear on the local display. + * * @hide */ - @TestApi + @SuppressLint("UnflaggedApi") // promoting from @TestApi. + @SystemApi int DISPLAY_IME_POLICY_LOCAL = 0; /** - * Display IME Policy: The IME should appear on the fallback display. + * Display IME Policy: The IME should appear on a fallback display. + * + * <p>The fallback display is always {@link Display#DEFAULT_DISPLAY}.</p> + * * @hide */ - @TestApi + @SuppressLint("UnflaggedApi") // promoting from @TestApi. + @SystemApi int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; /** * Display IME Policy: The IME should be hidden. * - * Setting this policy will prevent the IME from making a connection. This - * will prevent any IME from receiving metadata about input. + * <p>Setting this policy will prevent the IME from making a connection. This + * will prevent any IME from receiving metadata about input and this display will effectively + * have no IME.</p> + * * @hide */ - @TestApi + @SuppressLint("UnflaggedApi") // promoting from @TestApi. + @SystemApi int DISPLAY_IME_POLICY_HIDE = 2; /** @@ -3251,6 +3264,13 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 1 << 24; /** + * Flag to indicate that the window consumes the insets of {@link Type#ime()}. This makes + * windows below this window unable to receive visible IME insets. + * @hide + */ + public static final int PRIVATE_FLAG_CONSUME_IME_INSETS = 1 << 25; + + /** * Flag to indicate that the window is controlling the appearance of system bars. So we * don't need to adjust it by reading its system UI flags for compatibility. * @hide @@ -3334,6 +3354,7 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION, PRIVATE_FLAG_NOT_MAGNIFIABLE, PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, + PRIVATE_FLAG_CONSUME_IME_INSETS, PRIVATE_FLAG_APPEARANCE_CONTROLLED, PRIVATE_FLAG_BEHAVIOR_CONTROLLED, PRIVATE_FLAG_FIT_INSETS_CONTROLLED, @@ -3432,6 +3453,10 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, name = "COLOR_SPACE_AGNOSTIC"), @ViewDebug.FlagToString( + mask = PRIVATE_FLAG_CONSUME_IME_INSETS, + equals = PRIVATE_FLAG_CONSUME_IME_INSETS, + name = "CONSUME_IME_INSETS"), + @ViewDebug.FlagToString( mask = PRIVATE_FLAG_APPEARANCE_CONTROLLED, equals = PRIVATE_FLAG_APPEARANCE_CONTROLLED, name = "APPEARANCE_CONTROLLED"), @@ -3458,7 +3483,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.FlagToString( mask = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY, equals = PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY, - name = "PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY") + name = "SYSTEM_APPLICATION_OVERLAY") }) @PrivateFlags @TestApi @@ -5884,4 +5909,34 @@ public interface WindowManager extends ViewManager { default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) { throw new UnsupportedOperationException(); } + + /** + * Add a trusted presentation listener associated with a window. + * + * <p> If this listener is already registered then the window and thresholds will be updated. + * + * @param window The Window to add the trusted presentation listener for + * @param thresholds The {@link TrustedPresentationThresholds} that will specify + * when the to invoke the callback. + * @param executor The {@link Executor} where the callback will be invoked on. + * @param listener The {@link Consumer} that will receive the callbacks + * when entered or exited trusted presentation per the thresholds. + */ + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + default void registerTrustedPresentationListener(@NonNull IBinder window, + @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor, + @NonNull Consumer<Boolean> listener) { + throw new UnsupportedOperationException(); + } + + /** + * Removes a presentation listener associated with a window. If the listener was not previously + * registered, the call will be a noop. + * + * @see WindowManager#registerTrustedPresentationListener(IBinder, TrustedPresentationThresholds, Executor, Consumer) + */ + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + default void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { + throw new UnsupportedOperationException(); + } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 214f1ec3d1ec..f1e406196abf 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -30,9 +30,13 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.util.AndroidRuntimeException; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.view.inputmethod.InputMethodManager; +import android.window.ITrustedPresentationListener; +import android.window.TrustedPresentationThresholds; import com.android.internal.util.FastPrintWriter; @@ -43,6 +47,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.WeakHashMap; import java.util.concurrent.Executor; +import java.util.function.Consumer; import java.util.function.IntConsumer; /** @@ -143,6 +148,9 @@ public final class WindowManagerGlobal { private Runnable mSystemPropertyUpdater; + private final TrustedPresentationListener mTrustedPresentationListener = + new TrustedPresentationListener(); + private WindowManagerGlobal() { } @@ -324,7 +332,7 @@ public final class WindowManagerGlobal { final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags - & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } @@ -482,7 +490,7 @@ public final class WindowManagerGlobal { if (who != null) { WindowLeaked leak = new WindowLeaked( what + " " + who + " has leaked window " - + root.getView() + " that was originally added here"); + + root.getView() + " that was originally added here"); leak.setStackTrace(root.getLocation().getStackTrace()); Log.e(TAG, "", leak); } @@ -790,6 +798,87 @@ public final class WindowManagerGlobal { } } + public void registerTrustedPresentationListener(@NonNull IBinder window, + @NonNull TrustedPresentationThresholds thresholds, Executor executor, + @NonNull Consumer<Boolean> listener) { + mTrustedPresentationListener.addListener(window, thresholds, listener, executor); + } + + public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { + mTrustedPresentationListener.removeListener(listener); + } + + private final class TrustedPresentationListener extends + ITrustedPresentationListener.Stub { + private static int sId = 0; + private final ArrayMap<Consumer<Boolean>, Pair<Integer, Executor>> mListeners = + new ArrayMap<>(); + + private final Object mTplLock = new Object(); + + private void addListener(IBinder window, TrustedPresentationThresholds thresholds, + Consumer<Boolean> listener, Executor executor) { + synchronized (mTplLock) { + if (mListeners.containsKey(listener)) { + Log.i(TAG, "Updating listener " + listener + " thresholds to " + thresholds); + removeListener(listener); + } + int id = sId++; + mListeners.put(listener, new Pair<>(id, executor)); + try { + WindowManagerGlobal.getWindowManagerService() + .registerTrustedPresentationListener(window, this, thresholds, id); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + } + + private void removeListener(Consumer<Boolean> listener) { + synchronized (mTplLock) { + var removedListener = mListeners.remove(listener); + if (removedListener == null) { + Log.i(TAG, "listener " + listener + " does not exist."); + return; + } + + try { + WindowManagerGlobal.getWindowManagerService() + .unregisterTrustedPresentationListener(this, removedListener.first); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + } + + @Override + public void onTrustedPresentationChanged(int[] inTrustedStateListenerIds, + int[] outOfTrustedStateListenerIds) { + ArrayList<Runnable> firedListeners = new ArrayList<>(); + synchronized (mTplLock) { + mListeners.forEach((listener, idExecutorPair) -> { + final var listenerId = idExecutorPair.first; + final var executor = idExecutorPair.second; + for (int id : inTrustedStateListenerIds) { + if (listenerId == id) { + firedListeners.add(() -> executor.execute( + () -> listener.accept(/*presentationState*/true))); + } + } + for (int id : outOfTrustedStateListenerIds) { + if (listenerId == id) { + firedListeners.add(() -> executor.execute( + () -> listener.accept(/*presentationState*/false))); + } + } + }); + } + for (int i = 0; i < firedListeners.size(); i++) { + firedListeners.get(i).run(); + } + } + } + /** @hide */ public void addWindowlessRoot(ViewRootImpl impl) { synchronized (mLock) { @@ -801,7 +890,7 @@ public final class WindowManagerGlobal { public void removeWindowlessRoot(ViewRootImpl impl) { synchronized (mLock) { mWindowlessRoots.remove(impl); - } + } } public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index d7b74b3bcfe2..b4b1fde89a46 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -37,6 +37,7 @@ import android.os.StrictMode; import android.util.Log; import android.window.ITaskFpsCallback; import android.window.TaskFpsCallback; +import android.window.TrustedPresentationThresholds; import android.window.WindowContext; import android.window.WindowMetricsController; import android.window.WindowProvider; @@ -508,4 +509,17 @@ public final class WindowManagerImpl implements WindowManager { } return false; } + + @Override + public void registerTrustedPresentationListener(@NonNull IBinder window, + @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor, + @NonNull Consumer<Boolean> listener) { + mGlobal.registerTrustedPresentationListener(window, thresholds, executor, listener); + } + + @Override + public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { + mGlobal.unregisterTrustedPresentationListener(listener); + + } } diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 75ea08e5450b..e057660961f6 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -10,6 +10,13 @@ flag { } flag { + name: "a11y_qs_shortcut" + namespace: "accessibility" + description: "Add Quick Setting as one of the a11y shortcut options" + bug: "297554934" +} + +flag { namespace: "accessibility" name: "allow_shortcut_chooser_on_lockscreen" description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen" @@ -32,6 +39,13 @@ flag { flag { namespace: "accessibility" + name: "copy_events_for_gesture_detection" + description: "Creates copies of MotionEvents and GestureEvents in GestureMatcher" + bug: "280130713" +} + +flag { + namespace: "accessibility" name: "flash_notification_system_api" description: "Makes flash notification APIs as system APIs for calling from mainline module" bug: "303131332" @@ -70,4 +84,4 @@ flag { namespace: "accessibility" description: "Feature flag for system pinch zoom gesture detector and related opt-out apis" bug: "283323770" -}
\ No newline at end of file +} diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index fab8c7796dfd..1b7d57b785f5 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -20,8 +20,8 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static com.android.internal.inputmethod.InputMethodDebug.softInputDisplayReasonToString; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_HIDE_ANIMATION; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_IME_INSETS_SHOW_ANIMATION; +import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_HIDE_ANIMATION; +import static com.android.internal.jank.Cuj.CUJ_IME_INSETS_SHOW_ANIMATION; import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_HIDDEN; import static com.android.internal.util.LatencyTracker.ACTION_REQUEST_IME_SHOWN; @@ -758,7 +758,7 @@ public interface ImeTracker { * A helper method to translate animation type to CUJ type for IME animations. * * @param animType the animation type. - * @return the integer in {@link com.android.internal.jank.InteractionJankMonitor.CujType}, + * @return the integer in {@link com.android.internal.jank.Cuj.CujType}, * or {@code -1} if the animation type is not supported for tracking yet. */ private static int getImeInsetsCujFromAnimation(@AnimationType int animType) { diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 8b55494c75d4..d38a95e713b3 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -16,9 +16,11 @@ package android.view.inputmethod; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -113,6 +115,11 @@ public final class InputMethodInfo implements Parcelable { final boolean mIsVrOnly; /** + * IME only supports virtual devices. + */ + final boolean mIsVirtualDeviceOnly; + + /** * The unique string Id to identify the input method. This is generated * from the input method component. */ @@ -239,6 +246,7 @@ public final class InputMethodInfo implements Parcelable { String settingsActivityComponent = null; String stylusHandwritingSettingsActivity = null; boolean isVrOnly; + boolean isVirtualDeviceOnly; int isDefaultResId = 0; XmlResourceParser parser = null; @@ -277,6 +285,8 @@ public final class InputMethodInfo implements Parcelable { } isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false); + isVirtualDeviceOnly = sa.getBoolean( + com.android.internal.R.styleable.InputMethod_isVirtualDeviceOnly, false); isDefaultResId = sa.getResourceId( com.android.internal.R.styleable.InputMethod_isDefault, 0); supportsSwitchingToNextInputMethod = sa.getBoolean( @@ -382,6 +392,7 @@ public final class InputMethodInfo implements Parcelable { mSuppressesSpellChecker = suppressesSpellChecker; mShowInInputMethodPicker = showInInputMethodPicker; mIsVrOnly = isVrOnly; + mIsVirtualDeviceOnly = isVirtualDeviceOnly; } /** @@ -399,6 +410,7 @@ public final class InputMethodInfo implements Parcelable { mSuppressesSpellChecker = source.mSuppressesSpellChecker; mShowInInputMethodPicker = source.mShowInInputMethodPicker; mIsVrOnly = source.mIsVrOnly; + mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly; mService = source.mService; mSubtypes = source.mSubtypes; mHandledConfigChanges = source.mHandledConfigChanges; @@ -418,6 +430,7 @@ public final class InputMethodInfo implements Parcelable { mSuppressesSpellChecker = source.readBoolean(); mShowInInputMethodPicker = source.readBoolean(); mIsVrOnly = source.readBoolean(); + mIsVirtualDeviceOnly = source.readBoolean(); mService = ResolveInfo.CREATOR.createFromParcel(source); mSubtypes = new InputMethodSubtypeArray(source); mHandledConfigChanges = source.readInt(); @@ -435,7 +448,8 @@ public final class InputMethodInfo implements Parcelable { settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */, - 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, + false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */, + false /* supportsStylusHandwriting */, null /* stylusHandwritingSettingsActivityAttr */, false /* inlineSuggestionsEnabled */); } @@ -453,8 +467,9 @@ public final class InputMethodInfo implements Parcelable { settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */, - 0 /* handledConfigChanges */, supportStylusHandwriting, - stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */); + false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */, + supportStylusHandwriting, stylusHandwritingSettingsActivityAttr, + false /* inlineSuggestionsEnabled */); } /** @@ -468,7 +483,8 @@ public final class InputMethodInfo implements Parcelable { this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, - false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges, + false /* inlineSuggestionsEnabled */, false /* isVrOnly */, + false /* isVirtualDeviceOnly */, handledConfigChanges, false /* supportsStylusHandwriting */, null /* stylusHandwritingSettingsActivityAttr */, false /* inlineSuggestionsEnabled */); @@ -483,7 +499,7 @@ public final class InputMethodInfo implements Parcelable { boolean forceDefault) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, - false /* isVrOnly */, 0 /* handledconfigChanges */, + false /* isVrOnly */, false /* isVirtualDeviceOnly */, 0 /* handledconfigChanges */, false /* supportsStylusHandwriting */, null /* stylusHandwritingSettingsActivityAttr */, false /* inlineSuggestionsEnabled */); @@ -498,6 +514,7 @@ public final class InputMethodInfo implements Parcelable { boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly, + false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, null /* stylusHandwritingSettingsActivityAttr */, false /* inlineSuggestionsEnabled */); @@ -510,8 +527,8 @@ public final class InputMethodInfo implements Parcelable { public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, - boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting, - String stylusHandwritingSettingsActivityAttr, + boolean isVrOnly, boolean isVirtualDeviceOnly, int handledConfigChanges, + boolean supportsStylusHandwriting, String stylusHandwritingSettingsActivityAttr, boolean supportsInlineSuggestionsWithTouchExploration) { final ServiceInfo si = ri.serviceInfo; mService = ri; @@ -528,6 +545,7 @@ public final class InputMethodInfo implements Parcelable { mSuppressesSpellChecker = false; mShowInInputMethodPicker = true; mIsVrOnly = isVrOnly; + mIsVirtualDeviceOnly = isVirtualDeviceOnly; mHandledConfigChanges = handledConfigChanges; mSupportsStylusHandwriting = supportsStylusHandwriting; mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr; @@ -635,6 +653,16 @@ public final class InputMethodInfo implements Parcelable { } /** + * Returns true if IME supports only virtual devices. + * @hide + */ + @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME) + @SystemApi + public boolean isVirtualDeviceOnly() { + return mIsVirtualDeviceOnly; + } + + /** * Return the count of the subtypes of Input Method. */ public int getSubtypeCount() { @@ -732,6 +760,7 @@ public final class InputMethodInfo implements Parcelable { pw.println(prefix + "mId=" + mId + " mSettingsActivityName=" + mSettingsActivityName + " mIsVrOnly=" + mIsVrOnly + + " mIsVirtualDeviceOnly=" + mIsVirtualDeviceOnly + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled + " mSupportsInlineSuggestionsWithTouchExploration=" @@ -851,6 +880,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeBoolean(mSuppressesSpellChecker); dest.writeBoolean(mShowInInputMethodPicker); dest.writeBoolean(mIsVrOnly); + dest.writeBoolean(mIsVirtualDeviceOnly); mService.writeToParcel(dest, flags); mSubtypes.writeToParcel(dest); dest.writeInt(mHandledConfigChanges); diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values-sw600dp/config.xml b/core/java/android/window/ITrustedPresentationListener.aidl index be1f081d5b8f..b33128abb7e5 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values-sw600dp/config.xml +++ b/core/java/android/window/ITrustedPresentationListener.aidl @@ -1,13 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright (c) 2023, The Android Open Source Project +/* + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<resources> - <!-- If true, attach the navigation bar to the app during app transition --> - <bool name="config_attachNavBarToAppDuringTransition">false</bool> -</resources> + +package android.window; + +/** + * @hide + */ +oneway interface ITrustedPresentationListener { + void onTrustedPresentationChanged(in int[] enteredTrustedStateIds, in int[] exitedTrustedStateIds); +}
\ No newline at end of file diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values-sw600dp/config.xml b/core/java/android/window/TrustedPresentationListener.java index be1f081d5b8f..02fd6d98fb0d 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values-sw600dp/config.xml +++ b/core/java/android/window/TrustedPresentationListener.java @@ -1,13 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright (c) 2023, The Android Open Source Project +/* + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<resources> - <!-- If true, attach the navigation bar to the app during app transition --> - <bool name="config_attachNavBarToAppDuringTransition">false</bool> -</resources> + +package android.window; + +/** + * @hide + */ +public interface TrustedPresentationListener { + + void onTrustedPresentationChanged(boolean inTrustedPresentationState); + +} diff --git a/core/java/android/window/TrustedPresentationThresholds.aidl b/core/java/android/window/TrustedPresentationThresholds.aidl new file mode 100644 index 000000000000..d7088bf0fddc --- /dev/null +++ b/core/java/android/window/TrustedPresentationThresholds.aidl @@ -0,0 +1,3 @@ +package android.window; + +parcelable TrustedPresentationThresholds; diff --git a/core/java/android/window/TrustedPresentationThresholds.java b/core/java/android/window/TrustedPresentationThresholds.java new file mode 100644 index 000000000000..90f8834b37d1 --- /dev/null +++ b/core/java/android/window/TrustedPresentationThresholds.java @@ -0,0 +1,144 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.FlaggedApi; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.SurfaceControl; + +import androidx.annotation.NonNull; + +import com.android.window.flags.Flags; + +/** + * Threshold values that are sent with + * {@link android.view.WindowManager#registerTrustedPresentationListener(IBinder, + * TrustedPresentationThresholds, Executor, Consumer)} + */ +@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) +public final class TrustedPresentationThresholds implements Parcelable { + /** + * The min alpha the {@link SurfaceControl} is required to have to be considered inside the + * threshold. + */ + @FloatRange(from = 0f, fromInclusive = false, to = 1f) + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + @SuppressLint("InternalField") // simple data class + public final float minAlpha; + + /** + * The min fraction of the SurfaceControl that was presented to the user to be considered + * inside the threshold. + */ + @FloatRange(from = 0f, fromInclusive = false, to = 1f) + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + @SuppressLint("InternalField") // simple data class + public final float minFractionRendered; + + /** + * The time in milliseconds required for the {@link SurfaceControl} to be in the threshold. + */ + @IntRange(from = 1) + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + @SuppressLint("InternalField") // simple data class + public final int stabilityRequirementMs; + + private void checkValid() { + if (minAlpha <= 0 || minFractionRendered <= 0 || stabilityRequirementMs < 1) { + throw new IllegalArgumentException( + "TrustedPresentationThresholds values are invalid"); + } + } + + /** + * Creates a new TrustedPresentationThresholds. + * + * @param minAlpha The min alpha the {@link SurfaceControl} is required to + * have to be considered inside the + * threshold. + * @param minFractionRendered The min fraction of the SurfaceControl that was presented + * to the user to be considered + * inside the threshold. + * @param stabilityRequirementMs The time in milliseconds required for the + * {@link SurfaceControl} to be in the threshold. + */ + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public TrustedPresentationThresholds( + @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minAlpha, + @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minFractionRendered, + @IntRange(from = 1) int stabilityRequirementMs) { + this.minAlpha = minAlpha; + this.minFractionRendered = minFractionRendered; + this.stabilityRequirementMs = stabilityRequirementMs; + checkValid(); + } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public String toString() { + return "TrustedPresentationThresholds { " + + "minAlpha = " + minAlpha + ", " + + "minFractionRendered = " + minFractionRendered + ", " + + "stabilityRequirementMs = " + stabilityRequirementMs + + " }"; + } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeFloat(minAlpha); + dest.writeFloat(minFractionRendered); + dest.writeInt(stabilityRequirementMs); + } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public int describeContents() { + return 0; + } + + /** + * @hide + */ + TrustedPresentationThresholds(@NonNull Parcel in) { + minAlpha = in.readFloat(); + minFractionRendered = in.readFloat(); + stabilityRequirementMs = in.readInt(); + + checkValid(); + } + + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public static final @NonNull Creator<TrustedPresentationThresholds> CREATOR = + new Creator<TrustedPresentationThresholds>() { + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public TrustedPresentationThresholds[] newArray(int size) { + return new TrustedPresentationThresholds[size]; + } + + @Override + @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW) + public TrustedPresentationThresholds createFromParcel(@NonNull Parcel in) { + return new TrustedPresentationThresholds(in); + } + }; +} diff --git a/core/java/android/window/flags/accessibility.aconfig b/core/java/android/window/flags/accessibility.aconfig new file mode 100644 index 000000000000..d467be6e5311 --- /dev/null +++ b/core/java/android/window/flags/accessibility.aconfig @@ -0,0 +1,8 @@ +package: "com.android.window.flags" + +flag { + name: "do_not_check_intersection_when_non_magnifiable_window_transitions" + namespace: "accessibility" + description: "The flag controls whether the intersection check for non-magnifiable windows is needed when onWindowTransition," + bug: "312624253" +}
\ No newline at end of file diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig index f828cff14e88..ad0e9a487c53 100644 --- a/core/java/android/window/flags/responsible_apis.aconfig +++ b/core/java/android/window/flags/responsible_apis.aconfig @@ -41,3 +41,10 @@ flag { description: "Prevent a task to restart based on a visible window during task switch." bug: "171459802" } + +flag { + name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid" + namespace: "responsible_apis" + description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped." + bug: "171459802" +} diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 29932f342b74..56df49370379 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -56,3 +56,11 @@ flag { is_fixed_read_only: true bug: "308662081" } + +flag { + namespace: "window_surfaces" + name: "trusted_presentation_listener_for_window" + description: "Enable trustedPresentationListener on windows public API" + is_fixed_read_only: true + bug: "278027319" +}
\ No newline at end of file diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 1e0b2a0263cf..efc1455ecd45 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -80,7 +80,8 @@ public class SuspendedAppActivity extends AlertActivity // Suspension conditions were modified, dismiss any related visible dialogs. final String[] modified = intent.getStringArrayExtra( Intent.EXTRA_CHANGED_PACKAGE_LIST); - if (ArrayUtils.contains(modified, mSuspendedPackage)) { + if (ArrayUtils.contains(modified, mSuspendedPackage) + && !isPackageSuspended(mSuspendedPackage)) { if (!isFinishing()) { Slog.w(TAG, "Package " + mSuspendedPackage + " has modified" + " suspension conditions while dialog was visible. Finishing."); @@ -92,6 +93,15 @@ public class SuspendedAppActivity extends AlertActivity } }; + private boolean isPackageSuspended(String packageName) { + try { + return mPm.isPackageSuspended(packageName); + } catch (PackageManager.NameNotFoundException ne) { + Slog.e(TAG, "Package " + packageName + " not found", ne); + } + return false; + } + private CharSequence getAppLabel(String packageName) { try { return mPm.getApplicationInfoAsUser(packageName, 0, mUserId).loadLabel(mPm); diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index d85227f1a0a8..c89cfc4b0191 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -199,6 +199,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onPackageChangedWithExtras(String packageName, Bundle extras) { } + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit, + Bundle extras) { + return onHandleForceStop(intent, packages, uid, doit); + } + public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { return false; } @@ -254,6 +259,15 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onPackageModified(@NonNull String packageName) { } + /** + * Called when a package in the stopped state is started for some reason. + * + * @param packageName Name of the package that was unstopped + * @param uid UID of the package that was unstopped + */ + public void onPackageUnstopped(String packageName, int uid, Bundle extras) { + } + public boolean didSomePackagesChange() { return mSomePackagesChanged; } @@ -444,13 +458,13 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { mChangeType = PACKAGE_TEMPORARY_CHANGE; boolean canRestart = onHandleForceStop(intent, mDisappearingPackages, - intent.getIntExtra(Intent.EXTRA_UID, 0), false); + intent.getIntExtra(Intent.EXTRA_UID, 0), false, intent.getExtras()); if (canRestart) setResultCode(Activity.RESULT_OK); } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { mDisappearingPackages = new String[] {getPackageName(intent)}; mChangeType = PACKAGE_TEMPORARY_CHANGE; onHandleForceStop(intent, mDisappearingPackages, - intent.getIntExtra(Intent.EXTRA_UID, 0), true); + intent.getIntExtra(Intent.EXTRA_UID, 0), true, intent.getExtras()); } else if (Intent.ACTION_UID_REMOVED.equals(action)) { onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0)); } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { @@ -485,6 +499,12 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); mSomePackagesChanged = true; onPackagesUnsuspended(pkgList); + } else if (Intent.ACTION_PACKAGE_UNSTOPPED.equals(action)) { + final String pkgName = getPackageName(intent); + mAppearingPackages = new String[] {pkgName}; + mChangeType = PACKAGE_TEMPORARY_CHANGE; + onPackageUnstopped(pkgName, intent.getIntExtra(Intent.EXTRA_UID, 0), + intent.getExtras()); } if (mSomePackagesChanged) { diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java new file mode 100644 index 000000000000..f460233f0edd --- /dev/null +++ b/core/java/com/android/internal/jank/Cuj.java @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.jank; + +import android.annotation.IntDef; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** @hide */ +public class Cuj { + @VisibleForTesting + public static final int MAX_LENGTH_OF_CUJ_NAME = 80; + + // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. + public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; + public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2; + public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3; + public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4; + public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5; + public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6; + public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7; + public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8; + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9; + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10; + public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11; + public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12; + public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13; + public static final int CUJ_NOTIFICATION_ADD = 14; + public static final int CUJ_NOTIFICATION_REMOVE = 15; + public static final int CUJ_NOTIFICATION_APP_START = 16; + public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17; + public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18; + public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19; + public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20; + public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21; + public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22; + public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23; + public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24; + public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25; + public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26; + public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27; + public static final int CUJ_SETTINGS_PAGE_SCROLL = 28; + public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32; + public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33; + public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34; + public static final int CUJ_PIP_TRANSITION = 35; + public static final int CUJ_WALLPAPER_TRANSITION = 36; + public static final int CUJ_USER_SWITCH = 37; + public static final int CUJ_SPLASHSCREEN_AVD = 38; + public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39; + public static final int CUJ_SCREEN_OFF = 40; + public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41; + public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42; + public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43; + public static final int CUJ_UNFOLD_ANIM = 44; + public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = 45; + public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46; + public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47; + public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48; + public static final int CUJ_SPLIT_SCREEN_ENTER = 49; + public static final int CUJ_SPLIT_SCREEN_EXIT = 50; + public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved. + public static final int CUJ_SPLIT_SCREEN_RESIZE = 52; + public static final int CUJ_SETTINGS_SLIDER = 53; + public static final int CUJ_TAKE_SCREENSHOT = 54; + public static final int CUJ_VOLUME_CONTROL = 55; + public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56; + public static final int CUJ_SETTINGS_TOGGLE = 57; + public static final int CUJ_SHADE_DIALOG_OPEN = 58; + public static final int CUJ_USER_DIALOG_OPEN = 59; + public static final int CUJ_TASKBAR_EXPAND = 60; + public static final int CUJ_TASKBAR_COLLAPSE = 61; + public static final int CUJ_SHADE_CLEAR_ALL = 62; + public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63; + public static final int CUJ_LOCKSCREEN_OCCLUSION = 64; + public static final int CUJ_RECENTS_SCROLLING = 65; + public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66; + public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67; + public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68; + public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; + public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71; + // 72 - 77 are reserved for b/281564325. + + /** + * In some cases when we do not have any end-target, we play a simple slide-down animation. + * eg: Open an app from Overview/Task switcher such that there is no home-screen icon. + * eg: Exit the app using back gesture. + */ + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78; + public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80; + public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81; + + public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82; + + public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83; + + public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84; + public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85; + public static final int CUJ_PREDICTIVE_BACK_HOME = 86; + + // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE. + @VisibleForTesting + static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME; + + /** @hide */ + @IntDef({ + CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, + CUJ_NOTIFICATION_SHADE_SCROLL_FLING, + CUJ_NOTIFICATION_SHADE_ROW_EXPAND, + CUJ_NOTIFICATION_SHADE_ROW_SWIPE, + CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, + CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, + CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS, + CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON, + CUJ_LAUNCHER_APP_CLOSE_TO_HOME, + CUJ_LAUNCHER_APP_CLOSE_TO_PIP, + CUJ_LAUNCHER_QUICK_SWITCH, + CUJ_NOTIFICATION_HEADS_UP_APPEAR, + CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, + CUJ_NOTIFICATION_ADD, + CUJ_NOTIFICATION_REMOVE, + CUJ_NOTIFICATION_APP_START, + CUJ_LOCKSCREEN_PASSWORD_APPEAR, + CUJ_LOCKSCREEN_PATTERN_APPEAR, + CUJ_LOCKSCREEN_PIN_APPEAR, + CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR, + CUJ_LOCKSCREEN_PATTERN_DISAPPEAR, + CUJ_LOCKSCREEN_PIN_DISAPPEAR, + CUJ_LOCKSCREEN_TRANSITION_FROM_AOD, + CUJ_LOCKSCREEN_TRANSITION_TO_AOD, + CUJ_LAUNCHER_OPEN_ALL_APPS, + CUJ_LAUNCHER_ALL_APPS_SCROLL, + CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET, + CUJ_SETTINGS_PAGE_SCROLL, + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, + CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, + CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, + CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, + CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, + CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, + CUJ_PIP_TRANSITION, + CUJ_WALLPAPER_TRANSITION, + CUJ_USER_SWITCH, + CUJ_SPLASHSCREEN_AVD, + CUJ_SPLASHSCREEN_EXIT_ANIM, + CUJ_SCREEN_OFF, + CUJ_SCREEN_OFF_SHOW_AOD, + CUJ_ONE_HANDED_ENTER_TRANSITION, + CUJ_ONE_HANDED_EXIT_TRANSITION, + CUJ_UNFOLD_ANIM, + CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS, + CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS, + CUJ_SUW_LOADING_TO_NEXT_FLOW, + CUJ_SUW_LOADING_SCREEN_FOR_STATUS, + CUJ_SPLIT_SCREEN_ENTER, + CUJ_SPLIT_SCREEN_EXIT, + CUJ_LOCKSCREEN_LAUNCH_CAMERA, + CUJ_SPLIT_SCREEN_RESIZE, + CUJ_SETTINGS_SLIDER, + CUJ_TAKE_SCREENSHOT, + CUJ_VOLUME_CONTROL, + CUJ_BIOMETRIC_PROMPT_TRANSITION, + CUJ_SETTINGS_TOGGLE, + CUJ_SHADE_DIALOG_OPEN, + CUJ_USER_DIALOG_OPEN, + CUJ_TASKBAR_EXPAND, + CUJ_TASKBAR_COLLAPSE, + CUJ_SHADE_CLEAR_ALL, + CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, + CUJ_LOCKSCREEN_OCCLUSION, + CUJ_RECENTS_SCROLLING, + CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS, + CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE, + CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME, + CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION, + CUJ_LAUNCHER_OPEN_SEARCH_RESULT, + CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK, + CUJ_IME_INSETS_SHOW_ANIMATION, + CUJ_IME_INSETS_HIDE_ANIMATION, + CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER, + CUJ_LAUNCHER_UNFOLD_ANIM, + CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY, + CUJ_PREDICTIVE_BACK_CROSS_TASK, + CUJ_PREDICTIVE_BACK_HOME, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CujType { + } + + private static final int NO_STATSD_LOGGING = -1; + + // Used to convert CujType to InteractionType enum value for statsd logging. + // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd. + private static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = new int[LAST_CUJ + 1]; + static { + Arrays.fill(CUJ_TO_STATSD_INTERACTION_TYPE, NO_STATSD_LOGGING); + + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_SCROLL_FLING] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_PIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_QUICK_SWITCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_ADD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_REMOVE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_APP_START] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_APPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_DISAPPEAR] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_FROM_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_TO_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_ALL_APPS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_ALL_APPS_SCROLL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_PAGE_SCROLL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_UNLOCK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PIP_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_SWITCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_AVD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_EXIT_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF_SHOW_AOD] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_ENTER_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_EXIT_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_UNFOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_NEXT_FLOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_SCREEN_FOR_STATUS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_ENTER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_EXIT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_LAUNCH_CAMERA] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_RESIZE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_SLIDER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TAKE_SCREENSHOT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_VOLUME_CONTROL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BIOMETRIC_PROMPT_TRANSITION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_TOGGLE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_DIALOG_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_DIALOG_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_COLLAPSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_CLEAR_ALL] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_OCCLUSION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_RECENTS_SCROLLING] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; + } + + private Cuj() { + } + + /** + * A helper method to translate CUJ type to CUJ name. + * + * @param cujType the cuj type defined in this file + * @return the name of the cuj type + */ + public static String getNameOfCuj(int cujType) { + // Please note: + // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME. + // 2. The returned string should be the same with the name defined in atoms.proto. + switch (cujType) { + case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE: + return "NOTIFICATION_SHADE_EXPAND_COLLAPSE"; + case CUJ_NOTIFICATION_SHADE_SCROLL_FLING: + return "NOTIFICATION_SHADE_SCROLL_FLING"; + case CUJ_NOTIFICATION_SHADE_ROW_EXPAND: + return "NOTIFICATION_SHADE_ROW_EXPAND"; + case CUJ_NOTIFICATION_SHADE_ROW_SWIPE: + return "NOTIFICATION_SHADE_ROW_SWIPE"; + case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE: + return "NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE"; + case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE: + return "NOTIFICATION_SHADE_QS_SCROLL_SWIPE"; + case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS: + return "LAUNCHER_APP_LAUNCH_FROM_RECENTS"; + case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON: + return "LAUNCHER_APP_LAUNCH_FROM_ICON"; + case CUJ_LAUNCHER_APP_CLOSE_TO_HOME: + return "LAUNCHER_APP_CLOSE_TO_HOME"; + case CUJ_LAUNCHER_APP_CLOSE_TO_PIP: + return "LAUNCHER_APP_CLOSE_TO_PIP"; + case CUJ_LAUNCHER_QUICK_SWITCH: + return "LAUNCHER_QUICK_SWITCH"; + case CUJ_NOTIFICATION_HEADS_UP_APPEAR: + return "NOTIFICATION_HEADS_UP_APPEAR"; + case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR: + return "NOTIFICATION_HEADS_UP_DISAPPEAR"; + case CUJ_NOTIFICATION_ADD: + return "NOTIFICATION_ADD"; + case CUJ_NOTIFICATION_REMOVE: + return "NOTIFICATION_REMOVE"; + case CUJ_NOTIFICATION_APP_START: + return "NOTIFICATION_APP_START"; + case CUJ_LOCKSCREEN_PASSWORD_APPEAR: + return "LOCKSCREEN_PASSWORD_APPEAR"; + case CUJ_LOCKSCREEN_PATTERN_APPEAR: + return "LOCKSCREEN_PATTERN_APPEAR"; + case CUJ_LOCKSCREEN_PIN_APPEAR: + return "LOCKSCREEN_PIN_APPEAR"; + case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR: + return "LOCKSCREEN_PASSWORD_DISAPPEAR"; + case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR: + return "LOCKSCREEN_PATTERN_DISAPPEAR"; + case CUJ_LOCKSCREEN_PIN_DISAPPEAR: + return "LOCKSCREEN_PIN_DISAPPEAR"; + case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD: + return "LOCKSCREEN_TRANSITION_FROM_AOD"; + case CUJ_LOCKSCREEN_TRANSITION_TO_AOD: + return "LOCKSCREEN_TRANSITION_TO_AOD"; + case CUJ_LAUNCHER_OPEN_ALL_APPS : + return "LAUNCHER_OPEN_ALL_APPS"; + case CUJ_LAUNCHER_ALL_APPS_SCROLL: + return "LAUNCHER_ALL_APPS_SCROLL"; + case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET: + return "LAUNCHER_APP_LAUNCH_FROM_WIDGET"; + case CUJ_SETTINGS_PAGE_SCROLL: + return "SETTINGS_PAGE_SCROLL"; + case CUJ_LOCKSCREEN_UNLOCK_ANIMATION: + return "LOCKSCREEN_UNLOCK_ANIMATION"; + case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON: + return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON"; + case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER: + return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER"; + case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE: + return "SHADE_APP_LAUNCH_FROM_QS_TILE"; + case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON: + return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON"; + case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP: + return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP"; + case CUJ_PIP_TRANSITION: + return "PIP_TRANSITION"; + case CUJ_WALLPAPER_TRANSITION: + return "WALLPAPER_TRANSITION"; + case CUJ_USER_SWITCH: + return "USER_SWITCH"; + case CUJ_SPLASHSCREEN_AVD: + return "SPLASHSCREEN_AVD"; + case CUJ_SPLASHSCREEN_EXIT_ANIM: + return "SPLASHSCREEN_EXIT_ANIM"; + case CUJ_SCREEN_OFF: + return "SCREEN_OFF"; + case CUJ_SCREEN_OFF_SHOW_AOD: + return "SCREEN_OFF_SHOW_AOD"; + case CUJ_ONE_HANDED_ENTER_TRANSITION: + return "ONE_HANDED_ENTER_TRANSITION"; + case CUJ_ONE_HANDED_EXIT_TRANSITION: + return "ONE_HANDED_EXIT_TRANSITION"; + case CUJ_UNFOLD_ANIM: + return "UNFOLD_ANIM"; + case CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS: + return "SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS"; + case CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS: + return "SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS"; + case CUJ_SUW_LOADING_TO_NEXT_FLOW: + return "SUW_LOADING_TO_NEXT_FLOW"; + case CUJ_SUW_LOADING_SCREEN_FOR_STATUS: + return "SUW_LOADING_SCREEN_FOR_STATUS"; + case CUJ_SPLIT_SCREEN_ENTER: + return "SPLIT_SCREEN_ENTER"; + case CUJ_SPLIT_SCREEN_EXIT: + return "SPLIT_SCREEN_EXIT"; + case CUJ_LOCKSCREEN_LAUNCH_CAMERA: + return "LOCKSCREEN_LAUNCH_CAMERA"; + case CUJ_SPLIT_SCREEN_RESIZE: + return "SPLIT_SCREEN_RESIZE"; + case CUJ_SETTINGS_SLIDER: + return "SETTINGS_SLIDER"; + case CUJ_TAKE_SCREENSHOT: + return "TAKE_SCREENSHOT"; + case CUJ_VOLUME_CONTROL: + return "VOLUME_CONTROL"; + case CUJ_BIOMETRIC_PROMPT_TRANSITION: + return "BIOMETRIC_PROMPT_TRANSITION"; + case CUJ_SETTINGS_TOGGLE: + return "SETTINGS_TOGGLE"; + case CUJ_SHADE_DIALOG_OPEN: + return "SHADE_DIALOG_OPEN"; + case CUJ_USER_DIALOG_OPEN: + return "USER_DIALOG_OPEN"; + case CUJ_TASKBAR_EXPAND: + return "TASKBAR_EXPAND"; + case CUJ_TASKBAR_COLLAPSE: + return "TASKBAR_COLLAPSE"; + case CUJ_SHADE_CLEAR_ALL: + return "SHADE_CLEAR_ALL"; + case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION: + return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION"; + case CUJ_LOCKSCREEN_OCCLUSION: + return "LOCKSCREEN_OCCLUSION"; + case CUJ_RECENTS_SCROLLING: + return "RECENTS_SCROLLING"; + case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS: + return "LAUNCHER_APP_SWIPE_TO_RECENTS"; + case CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE: + return "LAUNCHER_CLOSE_ALL_APPS_SWIPE"; + case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME: + return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME"; + case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION: + return "LOCKSCREEN_CLOCK_MOVE_ANIMATION"; + case CUJ_LAUNCHER_OPEN_SEARCH_RESULT: + return "LAUNCHER_OPEN_SEARCH_RESULT"; + case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK: + return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK"; + case CUJ_IME_INSETS_SHOW_ANIMATION: + return "IME_INSETS_SHOW_ANIMATION"; + case CUJ_IME_INSETS_HIDE_ANIMATION: + return "IME_INSETS_HIDE_ANIMATION"; + case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER: + return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER"; + case CUJ_LAUNCHER_UNFOLD_ANIM: + return "LAUNCHER_UNFOLD_ANIM"; + case CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY: + return "PREDICTIVE_BACK_CROSS_ACTIVITY"; + case CUJ_PREDICTIVE_BACK_CROSS_TASK: + return "PREDICTIVE_BACK_CROSS_TASK"; + case CUJ_PREDICTIVE_BACK_HOME: + return "PREDICTIVE_BACK_HOME"; + } + return "UNKNOWN"; + } + + public static int getStatsdInteractionType(@CujType int cujType) { + return CUJ_TO_STATSD_INTERACTION_TYPE[cujType]; + } + + /** Returns whether the measurements for the given CUJ should be written to statsd. */ + public static boolean logToStatsd(@CujType int cujType) { + return getStatsdInteractionType(cujType) != NO_STATSD_LOGGING; + } + + /** + * A helper method to translate interaction type to CUJ name. + * + * @param interactionType the interaction type defined in AtomsProto.java + * @return the name of the interaction type + */ + public static String getNameOfInteraction(int interactionType) { + // There is an offset amount of 1 between cujType and interactionType. + return Cuj.getNameOfCuj(getCujTypeFromInteraction(interactionType)); + } + + /** + * A helper method to translate interaction type to CUJ type. + * + * @param interactionType the interaction type defined in AtomsProto.java + * @return the integer in {@link Cuj.CujType} + */ + private static int getCujTypeFromInteraction(int interactionType) { + return interactionType - 1; + } +} diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index c83452d88290..86729f74cd85 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -53,7 +53,6 @@ import android.view.WindowCallbacks; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.DisplayRefreshRate.RefreshRate; import com.android.internal.jank.InteractionJankMonitor.Configuration; -import com.android.internal.jank.InteractionJankMonitor.Session; import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; @@ -67,7 +66,6 @@ import java.util.concurrent.TimeUnit; public class FrameTracker extends SurfaceControl.OnJankDataListener implements HardwareRendererObserver.OnFrameMetricsAvailableListener { private static final String TAG = "FrameTracker"; - private static final boolean DEBUG = false; private static final long INVALID_ID = -1; public static final int NANOS_IN_MILLISECOND = 1_000_000; @@ -93,20 +91,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener REASON_CANCEL_NORMAL, REASON_CANCEL_NOT_BEGUN, REASON_CANCEL_SAME_VSYNC, + REASON_CANCEL_TIMEOUT, }) @Retention(RetentionPolicy.SOURCE) public @interface Reasons { } - @VisibleForTesting - public final InteractionJankMonitor mMonitor; private final HardwareRendererObserver mObserver; private final int mTraceThresholdMissedFrames; private final int mTraceThresholdFrameTimeMillis; private final ThreadedRendererWrapper mRendererWrapper; private final FrameMetricsWrapper mMetricsWrapper; private final SparseArray<JankInfo> mJankInfos = new SparseArray<>(); - private final Session mSession; + private final Configuration mConfig; private final ViewRootWrapper mViewRoot; private final SurfaceControlWrapper mSurfaceControlWrapper; private final int mDisplayId; @@ -197,19 +194,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } } - public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session, - @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer, + public FrameTracker(@NonNull Configuration config, + @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper, @NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull ChoreographerWrapper choreographer, @Nullable FrameMetricsWrapper metrics, @NonNull StatsLogWrapper statsLog, int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis, - @Nullable FrameTrackerListener listener, @NonNull Configuration config) { - mMonitor = monitor; + @Nullable FrameTrackerListener listener) { mSurfaceOnly = config.isSurfaceOnly(); - mSession = session; - mHandler = handler; + mConfig = config; + mHandler = config.getHandler(); mChoreographer = choreographer; mSurfaceControlWrapper = surfaceControlWrapper; mStatsLog = statsLog; @@ -222,7 +218,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mObserver = mSurfaceOnly ? null : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), - handler, /* waitForPresentTime= */ false); + mHandler, /* waitForPresentTime= */ false); mTraceThresholdMissedFrames = traceThresholdMissedFrames; mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; @@ -242,7 +238,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() { @Override public void surfaceCreated(SurfaceControl.Transaction t) { - getHandler().runWithScissors(() -> { + mHandler.runWithScissors(() -> { if (mSurfaceControl == null) { mSurfaceControl = mViewRoot.getSurfaceControl(); if (mBeginVsyncId != INVALID_ID) { @@ -262,13 +258,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // Wait a while to give the system a chance for the remaining // frames to arrive, then force finish the session. - getHandler().postDelayed(() -> { - if (DEBUG) { - Log.d(TAG, "surfaceDestroyed: " + mSession.getName() - + ", finalized=" + mMetricsFinalized - + ", info=" + mJankInfos.size() - + ", vsync=" + mBeginVsyncId); - } + mHandler.postDelayed(() -> { if (!mMetricsFinalized) { end(REASON_END_SURFACE_DESTROYED); finish(); @@ -282,11 +272,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } } - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public Handler getHandler() { - return mHandler; - } - /** * Begin a trace session of the CUJ. */ @@ -300,10 +285,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync; } if (mSurfaceControl != null) { - if (DEBUG) { - Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId - + ", defer=" + mDeferMonitoring + ", current=" + currentVsync); - } if (mDeferMonitoring && currentVsync < mBeginVsyncId) { markEvent("FT#deferMonitoring", 0); // Normal case, we begin the instrument from the very beginning, @@ -314,11 +295,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // there is no need to skip the frame where the begin invocation happens. beginInternal(); } - } else { - if (DEBUG) { - Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ=" - + mSession.getName()); - } } } @@ -336,8 +312,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return; } mTracingStarted = true; - Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, mSession.getName(), mSession.getName(), - (int) mBeginVsyncId); + String name = mConfig.getSessionName(); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, name, name, (int) mBeginVsyncId); markEvent("FT#beginVsync", mBeginVsyncId); markEvent("FT#layerId", mSurfaceControl.getLayerId()); mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); @@ -361,15 +337,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } else if (mEndVsyncId <= mBeginVsyncId) { return cancel(REASON_CANCEL_SAME_VSYNC); } else { - if (DEBUG) { - Log.d(TAG, "end: " + mSession.getName() - + ", end=" + mEndVsyncId + ", reason=" + reason); - } + final String name = mConfig.getSessionName(); markEvent("FT#end", reason); markEvent("FT#endVsync", mEndVsyncId); - Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(), - (int) mBeginVsyncId); - mSession.setReason(reason); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, name, (int) mBeginVsyncId); // We don't remove observer here, // will remove it when all the frame metrics in this duration are called back. @@ -395,16 +366,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mFlushAttempts++; } else { mWaitForFinishTimedOut = () -> { - Log.e(TAG, "force finish cuj, time out: " + mSession.getName()); + Log.e(TAG, "force finish cuj, time out: " + name); finish(); }; delay = TimeUnit.SECONDS.toMillis(10); } - getHandler().postDelayed(mWaitForFinishTimedOut, delay); + mHandler.postDelayed(mWaitForFinishTimedOut, delay); } }; - getHandler().postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND); - notifyCujEvent(ACTION_SESSION_END); + mHandler.postDelayed(mWaitForFinishTimedOut, FLUSH_DELAY_MILLISECOND); + notifyCujEvent(ACTION_SESSION_END, reason); return true; } } @@ -421,22 +392,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener markEvent("FT#cancel", reason); // We don't need to end the trace section if it has never begun. if (mTracingStarted) { - Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, mSession.getName(), - (int) mBeginVsyncId); + Trace.asyncTraceForTrackEnd( + Trace.TRACE_TAG_APP, mConfig.getSessionName(), (int) mBeginVsyncId); } // Always remove the observers in cancel call to avoid leakage. removeObservers(); - if (DEBUG) { - Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId - + ", end=" + mEndVsyncId + ", reason=" + reason); - } - - mSession.setReason(reason); // Notify the listener the session has been cancelled. // We don't notify the listeners if the session never begun. - notifyCujEvent(ACTION_SESSION_CANCEL); + notifyCujEvent(ACTION_SESSION_CANCEL, reason); return true; } @@ -455,13 +420,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener "The length of the trace event description <%s> exceeds %d", event, MAX_LENGTH_EVENT_DESC)); } - Trace.instantForTrack(Trace.TRACE_TAG_APP, mSession.getName(), event); + Trace.instantForTrack(Trace.TRACE_TAG_APP, mConfig.getSessionName(), event); } } - private void notifyCujEvent(String action) { + private void notifyCujEvent(String action, @Reasons int reason) { if (mListener == null) return; - mListener.onCujEvents(mSession, action); + mListener.onCujEvents(this, action, reason); } @Override @@ -496,7 +461,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener */ @VisibleForTesting public void postCallback(Runnable callback) { - getHandler().post(callback); + mHandler.post(callback); } @Nullable @@ -587,13 +552,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener if (mMetricsFinalized || mCancelled) return; mMetricsFinalized = true; - getHandler().removeCallbacks(mWaitForFinishTimedOut); + mHandler.removeCallbacks(mWaitForFinishTimedOut); mWaitForFinishTimedOut = null; markEvent("FT#finish", mJankInfos.size()); // The tracing has been ended, remove the observer, see if need to trigger perfetto. removeObservers(); + final String name = mConfig.getSessionName(); + int totalFramesCount = 0; long maxFrameTimeNanos = 0; int missedFramesCount = 0; @@ -616,7 +583,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener totalFramesCount++; boolean missedFrame = false; if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) { - Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName()); + Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name); missedAppFramesCount++; missedFrame = true; } @@ -625,7 +592,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0 || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0 || (info.jankType & PREDICTION_ERROR) != 0) { - Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName()); + Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name); missedSfFramesCount++; missedFrame = true; } @@ -646,7 +613,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener if (!mSurfaceOnly && !info.hwuiCallbackFired) { markEvent("FT#MissedHWUICallback", info.frameVsyncId); Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId - + ", CUJ=" + mSession.getName()); + + ", CUJ=" + name); } } if (!mSurfaceOnly && info.hwuiCallbackFired) { @@ -654,7 +621,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener if (!info.surfaceControlCallbackFired) { markEvent("FT#MissedSFCallback", info.frameVsyncId); Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId - + ", CUJ=" + mSession.getName()); + + ", CUJ=" + name); } } } @@ -662,29 +629,26 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener maxSuccessiveMissedFramesCount, successiveMissedFramesCount); // Log the frame stats as counters to make them easily accessible in traces. - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames", - missedFramesCount); - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedAppFrames", - missedAppFramesCount); - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedSfFrames", - missedSfFramesCount); - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames", - totalFramesCount); - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis", + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedFrames", missedFramesCount); + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedAppFrames", missedAppFramesCount); + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#missedSfFrames", missedSfFramesCount); + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#totalFrames", totalFramesCount); + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxFrameTimeMillis", (int) (maxFrameTimeNanos / NANOS_IN_MILLISECOND)); - Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxSuccessiveMissedFrames", + Trace.traceCounter(Trace.TRACE_TAG_APP, name + "#maxSuccessiveMissedFrames", maxSuccessiveMissedFramesCount); // Trigger perfetto if necessary. - if (shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) { - triggerPerfetto(); + if (mListener != null + && shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) { + mListener.triggerPerfetto(mConfig); } - if (mSession.logToStatsd()) { + if (mConfig.logToStatsd()) { mStatsLog.write( FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED, mDisplayId, refreshRate, - mSession.getStatsdInteractionType(), + mConfig.getStatsdInteractionType(), totalFramesCount, missedFramesCount, maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */ @@ -692,16 +656,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener missedAppFramesCount, maxSuccessiveMissedFramesCount); } - if (DEBUG) { - Log.i(TAG, "finish: CUJ=" + mSession.getName() - + " (" + mBeginVsyncId + "," + mEndVsyncId + ")" - + " totalFrames=" + totalFramesCount - + " missedAppFrames=" + missedAppFramesCount - + " missedSfFrames=" + missedSfFramesCount - + " missedFrames=" + missedFramesCount - + " maxFrameTimeMillis=" + maxFrameTimeNanos / NANOS_IN_MILLISECOND - + " maxSuccessiveMissedFramesCount=" + maxSuccessiveMissedFramesCount); - } } ThreadedRendererWrapper getThreadedRenderer() { @@ -737,13 +691,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } /** - * Trigger the prefetto daemon. - */ - public void triggerPerfetto() { - mMonitor.trigger(mSession); - } - - /** * A wrapper class that we can spy FrameMetrics (a final class) in unit tests. */ public static class FrameMetricsWrapper { @@ -895,9 +842,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener /** * Notify that the CUJ session was created. * - * @param session the CUJ session + * @param tracker the tracker * @param action the specific action + * @param reason the reason for the action + */ + void onCujEvents(FrameTracker tracker, String action, @Reasons int reason); + + /** + * Notify that the Perfetto trace should be triggered. + * + * @param config the tracker configuration */ - void onCujEvents(Session session, String action); + void triggerPerfetto(Configuration config); } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index e6b036cfaa19..8b1879f5225d 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -23,89 +23,9 @@ import static android.provider.DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT; import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; -import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL; -import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION; import android.Manifest; import android.annotation.ColorInt; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.UiThread; @@ -137,34 +57,31 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ViewRootWrapper; import com.android.internal.util.PerfettoTrigger; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.time.Instant; -import java.util.Locale; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; /** - * This class let users to begin and end the always on tracing mechanism. + * This class lets users begin and end the always on tracing mechanism. * * Enabling for local development: - * + *<pre> * adb shell device_config put interaction_jank_monitor enabled true * adb shell device_config put interaction_jank_monitor sampling_interval 1 - * + * </pre> * On debuggable builds, an overlay can be used to display the name of the * currently running cuj using: - * + * <pre> * adb shell device_config put interaction_jank_monitor debug_overlay_enabled true - * - * NOTE: The overlay will interfere with metrics, so it should only be used - * for understanding which UI events correspeond to which CUJs. + * </pre> + * <b>NOTE</b>: The overlay will interfere with metrics, so it should only be used + * for understanding which UI events correspond to which CUJs. * * @hide */ public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); - private static final boolean DEBUG = false; private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName(); private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; @@ -186,218 +103,79 @@ public class InteractionJankMonitor { private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; private static final boolean DEFAULT_DEBUG_OVERLAY_ENABLED = false; - @VisibleForTesting - public static final int MAX_LENGTH_OF_CUJ_NAME = 80; private static final int MAX_LENGTH_SESSION_NAME = 100; public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END"; public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL"; - // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. - public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; - public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2; - public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3; - public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4; - public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5; - public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6; - public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7; - public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8; - public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9; - public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10; - public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11; - public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12; - public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13; - public static final int CUJ_NOTIFICATION_ADD = 14; - public static final int CUJ_NOTIFICATION_REMOVE = 15; - public static final int CUJ_NOTIFICATION_APP_START = 16; - public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = 17; - public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = 18; - public static final int CUJ_LOCKSCREEN_PIN_APPEAR = 19; - public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = 20; - public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = 21; - public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22; - public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23; - public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24; - public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25; - public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26; - public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27; - public static final int CUJ_SETTINGS_PAGE_SCROLL = 28; - public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29; - public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30; - public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31; - public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32; - public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33; - public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34; - public static final int CUJ_PIP_TRANSITION = 35; - public static final int CUJ_WALLPAPER_TRANSITION = 36; - public static final int CUJ_USER_SWITCH = 37; - public static final int CUJ_SPLASHSCREEN_AVD = 38; - public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39; - public static final int CUJ_SCREEN_OFF = 40; - public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41; - public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42; - public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43; - public static final int CUJ_UNFOLD_ANIM = 44; - public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = 45; - public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = 46; - public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = 47; - public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48; - public static final int CUJ_SPLIT_SCREEN_ENTER = 49; - public static final int CUJ_SPLIT_SCREEN_EXIT = 50; - public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved. - public static final int CUJ_SPLIT_SCREEN_RESIZE = 52; - public static final int CUJ_SETTINGS_SLIDER = 53; - public static final int CUJ_TAKE_SCREENSHOT = 54; - public static final int CUJ_VOLUME_CONTROL = 55; - public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56; - public static final int CUJ_SETTINGS_TOGGLE = 57; - public static final int CUJ_SHADE_DIALOG_OPEN = 58; - public static final int CUJ_USER_DIALOG_OPEN = 59; - public static final int CUJ_TASKBAR_EXPAND = 60; - public static final int CUJ_TASKBAR_COLLAPSE = 61; - public static final int CUJ_SHADE_CLEAR_ALL = 62; - public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63; - public static final int CUJ_LOCKSCREEN_OCCLUSION = 64; - public static final int CUJ_RECENTS_SCROLLING = 65; - public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66; - public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67; - public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68; - public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; - public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71; - // 72 - 77 are reserved for b/281564325. - - /** - * In some cases when we do not have any end-target, we play a simple slide-down animation. - * eg: Open an app from Overview/Task switcher such that there is no home-screen icon. - * eg: Exit the app using back gesture. - */ - public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78; - public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80; - public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81; - - public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82; - - public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83; - - public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84; - public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85; - public static final int CUJ_PREDICTIVE_BACK_HOME = 86; - - private static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME; - private static final int NO_STATSD_LOGGING = -1; - - // Used to convert CujType to InteractionType enum value for statsd logging. - // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd. - @VisibleForTesting - public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = new int[LAST_CUJ + 1]; - - static { - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE; - CUJ_TO_STATSD_INTERACTION_TYPE[1] = NO_STATSD_LOGGING; // This is deprecated. - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_SCROLL_FLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_ROW_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_PIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_QUICK_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_ADD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_REMOVE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_APP_START] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_APPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_APPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PATTERN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_DISAPPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_PIN_DISAPPEAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_FROM_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_TRANSITION_TO_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_ALL_APPS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_ALL_APPS_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_PAGE_SCROLL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_UNLOCK_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PIP_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_SWITCH] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_AVD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLASHSCREEN_EXIT_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SCREEN_OFF_SHOW_AOD] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_ENTER_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_ONE_HANDED_EXIT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_TO_NEXT_FLOW] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_NEXT_FLOW; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SUW_LOADING_SCREEN_FOR_STATUS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_ENTER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_EXIT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_LAUNCH_CAMERA] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_RESIZE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_SLIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TAKE_SCREENSHOT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_VOLUME_CONTROL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BIOMETRIC_PROMPT_TRANSITION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SETTINGS_TOGGLE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_USER_DIALOG_OPEN] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_EXPAND] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_TASKBAR_COLLAPSE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_CLEAR_ALL] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_OCCLUSION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_RECENTS_SCROLLING] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__RECENTS_SCROLLING; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME; - CUJ_TO_STATSD_INTERACTION_TYPE[69] = NO_STATSD_LOGGING; // This is deprecated. - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT; - // 72 - 77 are reserved for b/281564325. - CUJ_TO_STATSD_INTERACTION_TYPE[72] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[73] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[74] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[75] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[76] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[77] = NO_STATSD_LOGGING; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; - CUJ_TO_STATSD_INTERACTION_TYPE[79] = NO_STATSD_LOGGING; // This is deprecated. - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = - UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = - UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; - CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = - UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; - } + // These are not the CUJ constants you are looking for. These constants simply forward their + // definition from {@link Cuj}. They are here only as a transition measure until all references + // have been updated to the new location. + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING; + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND; + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; + @Deprecated public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE; + @Deprecated public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; + @Deprecated public static final int CUJ_LAUNCHER_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH; + @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR; + @Deprecated public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR; + @Deprecated public static final int CUJ_NOTIFICATION_ADD = Cuj.CUJ_NOTIFICATION_ADD; + @Deprecated public static final int CUJ_NOTIFICATION_REMOVE = Cuj.CUJ_NOTIFICATION_REMOVE; + @Deprecated public static final int CUJ_NOTIFICATION_APP_START = Cuj.CUJ_NOTIFICATION_APP_START; + @Deprecated public static final int CUJ_LOCKSCREEN_PASSWORD_APPEAR = Cuj.CUJ_LOCKSCREEN_PASSWORD_APPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_PATTERN_APPEAR = Cuj.CUJ_LOCKSCREEN_PATTERN_APPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_PIN_APPEAR = Cuj.CUJ_LOCKSCREEN_PIN_APPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_PATTERN_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR; + @Deprecated public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = Cuj.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; + @Deprecated public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = Cuj.CUJ_LOCKSCREEN_TRANSITION_TO_AOD; + @Deprecated public static final int CUJ_SETTINGS_PAGE_SCROLL = Cuj.CUJ_SETTINGS_PAGE_SCROLL; + @Deprecated public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = Cuj.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; + @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON; + @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; + @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE; + @Deprecated public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = Cuj.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; + @Deprecated public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP; + @Deprecated public static final int CUJ_PIP_TRANSITION = Cuj.CUJ_PIP_TRANSITION; + @Deprecated public static final int CUJ_USER_SWITCH = Cuj.CUJ_USER_SWITCH; + @Deprecated public static final int CUJ_SPLASHSCREEN_AVD = Cuj.CUJ_SPLASHSCREEN_AVD; + @Deprecated public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = Cuj.CUJ_SPLASHSCREEN_EXIT_ANIM; + @Deprecated public static final int CUJ_SCREEN_OFF = Cuj.CUJ_SCREEN_OFF; + @Deprecated public static final int CUJ_SCREEN_OFF_SHOW_AOD = Cuj.CUJ_SCREEN_OFF_SHOW_AOD; + @Deprecated public static final int CUJ_UNFOLD_ANIM = Cuj.CUJ_UNFOLD_ANIM; + @Deprecated public static final int CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS = Cuj.CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS; + @Deprecated public static final int CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS = Cuj.CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS; + @Deprecated public static final int CUJ_SUW_LOADING_TO_NEXT_FLOW = Cuj.CUJ_SUW_LOADING_TO_NEXT_FLOW; + @Deprecated public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = Cuj.CUJ_SUW_LOADING_SCREEN_FOR_STATUS; + @Deprecated public static final int CUJ_SPLIT_SCREEN_RESIZE = Cuj.CUJ_SPLIT_SCREEN_RESIZE; + @Deprecated public static final int CUJ_SETTINGS_SLIDER = Cuj.CUJ_SETTINGS_SLIDER; + @Deprecated public static final int CUJ_TAKE_SCREENSHOT = Cuj.CUJ_TAKE_SCREENSHOT; + @Deprecated public static final int CUJ_VOLUME_CONTROL = Cuj.CUJ_VOLUME_CONTROL; + @Deprecated public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = Cuj.CUJ_BIOMETRIC_PROMPT_TRANSITION; + @Deprecated public static final int CUJ_SETTINGS_TOGGLE = Cuj.CUJ_SETTINGS_TOGGLE; + @Deprecated public static final int CUJ_SHADE_DIALOG_OPEN = Cuj.CUJ_SHADE_DIALOG_OPEN; + @Deprecated public static final int CUJ_USER_DIALOG_OPEN = Cuj.CUJ_USER_DIALOG_OPEN; + @Deprecated public static final int CUJ_TASKBAR_EXPAND = Cuj.CUJ_TASKBAR_EXPAND; + @Deprecated public static final int CUJ_TASKBAR_COLLAPSE = Cuj.CUJ_TASKBAR_COLLAPSE; + @Deprecated public static final int CUJ_SHADE_CLEAR_ALL = Cuj.CUJ_SHADE_CLEAR_ALL; + @Deprecated public static final int CUJ_LOCKSCREEN_OCCLUSION = Cuj.CUJ_LOCKSCREEN_OCCLUSION; + @Deprecated public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = Cuj.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION; + @Deprecated public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = Cuj.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER; + @Deprecated public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY; + @Deprecated public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = Cuj.CUJ_PREDICTIVE_BACK_CROSS_TASK; + @Deprecated public static final int CUJ_PREDICTIVE_BACK_HOME = Cuj.CUJ_PREDICTIVE_BACK_HOME; private static class InstanceHolder { public static final InteractionJankMonitor INSTANCE = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME)); } - private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = - this::updateProperties; - @GuardedBy("mLock") - private final SparseArray<FrameTracker> mRunningTrackers; - @GuardedBy("mLock") - private final SparseArray<Runnable> mTimeoutActions; - private final HandlerThread mWorker; + private final SparseArray<RunningTracker> mRunningTrackers = new SparseArray<>(); + private final Handler mWorker; private final DisplayResolutionTracker mDisplayResolutionTracker; private final Object mLock = new Object(); private @ColorInt int mDebugBgColor = Color.CYAN; @@ -409,91 +187,6 @@ public class InteractionJankMonitor { private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES; private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS; - /** @hide */ - @IntDef({ - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, - CUJ_NOTIFICATION_SHADE_SCROLL_FLING, - CUJ_NOTIFICATION_SHADE_ROW_EXPAND, - CUJ_NOTIFICATION_SHADE_ROW_SWIPE, - CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, - CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, - CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS, - CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON, - CUJ_LAUNCHER_APP_CLOSE_TO_HOME, - CUJ_LAUNCHER_APP_CLOSE_TO_PIP, - CUJ_LAUNCHER_QUICK_SWITCH, - CUJ_NOTIFICATION_HEADS_UP_APPEAR, - CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, - CUJ_NOTIFICATION_ADD, - CUJ_NOTIFICATION_REMOVE, - CUJ_NOTIFICATION_APP_START, - CUJ_LOCKSCREEN_PASSWORD_APPEAR, - CUJ_LOCKSCREEN_PATTERN_APPEAR, - CUJ_LOCKSCREEN_PIN_APPEAR, - CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR, - CUJ_LOCKSCREEN_PATTERN_DISAPPEAR, - CUJ_LOCKSCREEN_PIN_DISAPPEAR, - CUJ_LOCKSCREEN_TRANSITION_FROM_AOD, - CUJ_LOCKSCREEN_TRANSITION_TO_AOD, - CUJ_LAUNCHER_OPEN_ALL_APPS, - CUJ_LAUNCHER_ALL_APPS_SCROLL, - CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET, - CUJ_SETTINGS_PAGE_SCROLL, - CUJ_LOCKSCREEN_UNLOCK_ANIMATION, - CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON, - CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER, - CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE, - CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON, - CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, - CUJ_PIP_TRANSITION, - CUJ_WALLPAPER_TRANSITION, - CUJ_USER_SWITCH, - CUJ_SPLASHSCREEN_AVD, - CUJ_SPLASHSCREEN_EXIT_ANIM, - CUJ_SCREEN_OFF, - CUJ_SCREEN_OFF_SHOW_AOD, - CUJ_ONE_HANDED_ENTER_TRANSITION, - CUJ_ONE_HANDED_EXIT_TRANSITION, - CUJ_UNFOLD_ANIM, - CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS, - CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS, - CUJ_SUW_LOADING_TO_NEXT_FLOW, - CUJ_SUW_LOADING_SCREEN_FOR_STATUS, - CUJ_SPLIT_SCREEN_ENTER, - CUJ_SPLIT_SCREEN_EXIT, - CUJ_LOCKSCREEN_LAUNCH_CAMERA, - CUJ_SPLIT_SCREEN_RESIZE, - CUJ_SETTINGS_SLIDER, - CUJ_TAKE_SCREENSHOT, - CUJ_VOLUME_CONTROL, - CUJ_BIOMETRIC_PROMPT_TRANSITION, - CUJ_SETTINGS_TOGGLE, - CUJ_SHADE_DIALOG_OPEN, - CUJ_USER_DIALOG_OPEN, - CUJ_TASKBAR_EXPAND, - CUJ_TASKBAR_COLLAPSE, - CUJ_SHADE_CLEAR_ALL, - CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, - CUJ_LOCKSCREEN_OCCLUSION, - CUJ_RECENTS_SCROLLING, - CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS, - CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE, - CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME, - CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION, - CUJ_LAUNCHER_OPEN_SEARCH_RESULT, - CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK, - CUJ_IME_INSETS_SHOW_ANIMATION, - CUJ_IME_INSETS_HIDE_ANIMATION, - CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER, - CUJ_LAUNCHER_UNFOLD_ANIM, - CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY, - CUJ_PREDICTIVE_BACK_CROSS_TASK, - CUJ_PREDICTIVE_BACK_HOME, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CujType { - } - /** * Get the singleton of InteractionJankMonitor. * @@ -511,71 +204,44 @@ public class InteractionJankMonitor { @VisibleForTesting @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) public InteractionJankMonitor(@NonNull HandlerThread worker) { - mRunningTrackers = new SparseArray<>(); - mTimeoutActions = new SparseArray<>(); - mWorker = worker; - mWorker.start(); - mDisplayResolutionTracker = new DisplayResolutionTracker(worker.getThreadHandler()); - mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; - mEnabled = DEFAULT_ENABLED; + worker.start(); + mWorker = worker.getThreadHandler(); + mDisplayResolutionTracker = new DisplayResolutionTracker(mWorker); final Context context = ActivityThread.currentApplication(); - if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) { - if (DEBUG) { - Log.d(TAG, "Initialized the InteractionJankMonitor." - + " (No READ_DEVICE_CONFIG permission to change configs)" - + " enabled=" + mEnabled + ", interval=" + mSamplingInterval - + ", missedFrameThreshold=" + mTraceThresholdMissedFrames - + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis - + ", package=" + context.getPackageName()); - } + if (context == null || context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) { + Log.w(TAG, "Initializing without READ_DEVICE_CONFIG permission." + + " enabled=" + mEnabled + ", interval=" + mSamplingInterval + + ", missedFrameThreshold=" + mTraceThresholdMissedFrames + + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis + + ", package=" + (context == null ? "null" : context.getPackageName())); return; } // Post initialization to the background in case we're running on the main thread. - mWorker.getThreadHandler().post( - () -> { - try { - mPropertiesChangedListener.onPropertiesChanged( - DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR)); - DeviceConfig.addOnPropertiesChangedListener( - NAMESPACE_INTERACTION_JANK_MONITOR, - new HandlerExecutor(mWorker.getThreadHandler()), - mPropertiesChangedListener); - } catch (SecurityException ex) { - Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted=" - + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) - + ", package=" + context.getPackageName()); - } - }); + mWorker.post(() -> { + try { + updateProperties(DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR)); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_INTERACTION_JANK_MONITOR, + new HandlerExecutor(mWorker), this::updateProperties); + } catch (SecurityException ex) { + Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted=" + + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) + + ", package=" + context.getPackageName()); + } + }); } /** * Creates a {@link FrameTracker} instance. * - * @param config the config used in instrumenting - * @param session the session associates with this tracker + * @param config the conifg associates with this tracker * @return instance of the FrameTracker */ @VisibleForTesting - public FrameTracker createFrameTracker(Configuration config, Session session) { + public FrameTracker createFrameTracker(Configuration config) { final View view = config.mView; - if (!config.hasValidView()) { - boolean attached = false; - boolean hasViewRoot = false; - boolean hasRenderer = false; - if (view != null) { - attached = view.isAttachedToWindow(); - hasViewRoot = view.getViewRootImpl() != null; - hasRenderer = view.getThreadedRenderer() != null; - } - Log.d(TAG, "create FrameTracker fails: view=" + view - + ", attached=" + attached + ", hasViewRoot=" + hasViewRoot - + ", hasRenderer=" + hasRenderer, new Throwable()); - return null; - } - final ThreadedRendererWrapper threadedRenderer = view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer()); final ViewRootWrapper viewRoot = @@ -583,52 +249,50 @@ public class InteractionJankMonitor { final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper(); final ChoreographerWrapper choreographer = new ChoreographerWrapper(Choreographer.getInstance()); - final FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s); + final FrameTrackerListener eventsListener = new FrameTrackerListener() { + @Override + public void onCujEvents(FrameTracker tracker, String action, int reason) { + config.getHandler().runWithScissors(() -> + handleCujEvents(config.mCujType, tracker, action, reason), + EXECUTOR_TASK_TIMEOUT); + } + + @Override + public void triggerPerfetto(Configuration config) { + mWorker.post(() -> PerfettoTrigger.trigger(config.getPerfettoTrigger())); + } + }; final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper(); - return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot, + return new FrameTracker(config, threadedRenderer, viewRoot, surfaceControl, choreographer, frameMetrics, new FrameTracker.StatsLogWrapper(mDisplayResolutionTracker), mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, - eventsListener, config); + eventsListener); } @UiThread - private void handleCujEvents(String action, Session session) { + private void handleCujEvents( + @Cuj.CujType int cuj, FrameTracker tracker, String action, @Reasons int reason) { // Clear the running and timeout tasks if the end / cancel was fired within the tracker. // Or we might have memory leaks. - if (needRemoveTasks(action, session)) { - getTracker(session.getCuj()).getHandler().runWithScissors(() -> { - removeTimeout(session.getCuj()); - removeTracker(session.getCuj(), session.getReason()); - }, EXECUTOR_TASK_TIMEOUT); + if (needRemoveTasks(action, reason)) { + removeTrackerIfCurrent(cuj, tracker, reason); } } - private boolean needRemoveTasks(String action, Session session) { - final boolean badEnd = action.equals(ACTION_SESSION_END) - && session.getReason() != REASON_END_NORMAL; + private static boolean needRemoveTasks(String action, @Reasons int reason) { + final boolean badEnd = action.equals(ACTION_SESSION_END) && reason != REASON_END_NORMAL; final boolean badCancel = action.equals(ACTION_SESSION_CANCEL) - && !(session.getReason() == REASON_CANCEL_NORMAL - || session.getReason() == REASON_CANCEL_TIMEOUT); + && !(reason == REASON_CANCEL_NORMAL || reason == REASON_CANCEL_TIMEOUT); return badEnd || badCancel; } - private void removeTimeout(@CujType int cujType) { - synchronized (mLock) { - Runnable timeout = mTimeoutActions.get(cujType); - if (timeout != null) { - getTracker(cujType).getHandler().removeCallbacks(timeout); - mTimeoutActions.remove(cujType); - } - } - } - /** * @param cujType cuj type * @return true if the cuj is under instrumenting, false otherwise. */ - public boolean isInstrumenting(@CujType int cujType) { + public boolean isInstrumenting(@Cuj.CujType int cujType) { synchronized (mLock) { return mRunningTrackers.contains(cujType); } @@ -638,10 +302,10 @@ public class InteractionJankMonitor { * Begins a trace session. * * @param v an attached view. - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. * @return boolean true if the tracker is started successfully, false otherwise. */ - public boolean begin(View v, @CujType int cujType) { + public boolean begin(View v, @Cuj.CujType int cujType) { try { return begin(Configuration.Builder.withView(cujType, v)); } catch (IllegalArgumentException ex) { @@ -667,7 +331,7 @@ public class InteractionJankMonitor { final boolean success = config.getHandler().runWithScissors( () -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT); if (!success) { - Log.d(TAG, "begin failed due to timeout, CUJ=" + getNameOfCuj(config.mCujType)); + Log.d(TAG, "begin failed due to timeout, CUJ=" + Cuj.getNameOfCuj(config.mCujType)); return false; } return result.mResult; @@ -680,75 +344,59 @@ public class InteractionJankMonitor { @UiThread private boolean beginInternal(@NonNull Configuration conf) { int cujType = conf.mCujType; - if (!shouldMonitor(cujType)) return false; - FrameTracker tracker = getTracker(cujType); - // Skip subsequent calls if we already have an ongoing tracing. - if (tracker != null) return false; + if (!shouldMonitor()) { + return false; + } - // begin a new trace session. - tracker = createFrameTracker(conf, new Session(cujType, conf.mTag)); - if (tracker == null) return false; - putTracker(cujType, tracker); - tracker.begin(); + RunningTracker tracker = putTrackerIfNoCurrent(cujType, () -> + new RunningTracker( + conf, createFrameTracker(conf), () -> cancel(cujType, REASON_CANCEL_TIMEOUT))); + if (tracker == null) { + return false; + } + tracker.mTracker.begin(); // Cancel the trace if we don't get an end() call in specified duration. - scheduleTimeoutAction( - cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT)); + scheduleTimeoutAction(tracker.mConfig, tracker.mTimeoutAction); + return true; } /** * Check if the monitoring is enabled and if it should be sampled. */ - @SuppressWarnings("RandomModInteger") @VisibleForTesting - public boolean shouldMonitor(@CujType int cujType) { - boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; - if (!mEnabled || !shouldSample) { - if (DEBUG) { - Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType) - + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED - + ", sample=" + shouldSample + ", interval=" + mSamplingInterval); - } - return false; - } - return true; + public boolean shouldMonitor() { + return mEnabled && (ThreadLocalRandom.current().nextInt(mSamplingInterval) == 0); } - /** - * Schedules a timeout action. - * @param cuj cuj type - * @param timeout duration to timeout - * @param action action once timeout - */ @VisibleForTesting - public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) { - synchronized (mLock) { - mTimeoutActions.put(cuj, action); - getTracker(cuj).getHandler().postDelayed(action, timeout); - } + public void scheduleTimeoutAction(Configuration config, Runnable action) { + config.getHandler().postDelayed(action, config.mTimeout); } /** * Ends a trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. * @return boolean true if the tracker is ended successfully, false otherwise. */ - public boolean end(@CujType int cujType) { + public boolean end(@Cuj.CujType int cujType) { postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> { EventLogTags.writeJankCujEventsEndRequest( cujType, unixNanos, elapsedNanos, realtimeNanos); }); - FrameTracker tracker = getTracker(cujType); + RunningTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. - if (tracker == null) return false; + if (tracker == null) { + return false; + } try { final TrackerResult result = new TrackerResult(); - final boolean success = tracker.getHandler().runWithScissors( - () -> result.mResult = endInternal(cujType), EXECUTOR_TASK_TIMEOUT); + final boolean success = tracker.mConfig.getHandler().runWithScissors( + () -> result.mResult = endInternal(tracker), EXECUTOR_TASK_TIMEOUT); if (!success) { - Log.d(TAG, "end failed due to timeout, CUJ=" + getNameOfCuj(cujType)); + Log.d(TAG, "end failed due to timeout, CUJ=" + Cuj.getNameOfCuj(cujType)); return false; } return result.mResult; @@ -759,15 +407,11 @@ public class InteractionJankMonitor { } @UiThread - private boolean endInternal(@CujType int cujType) { - // remove the timeout action first. - removeTimeout(cujType); - FrameTracker tracker = getTracker(cujType); - if (tracker == null) return false; - // if the end call doesn't return true, another thread is handling end of the cuj. - if (tracker.end(REASON_END_NORMAL)) { - removeTracker(cujType, REASON_END_NORMAL); + private boolean endInternal(RunningTracker tracker) { + if (removeTrackerIfCurrent(tracker, REASON_END_NORMAL)) { + return false; } + tracker.mTracker.end(REASON_END_NORMAL); return true; } @@ -776,7 +420,7 @@ public class InteractionJankMonitor { * * @return boolean true if the tracker is cancelled successfully, false otherwise. */ - public boolean cancel(@CujType int cujType) { + public boolean cancel(@Cuj.CujType int cujType) { postEventLogToWorkerThread((unixNanos, elapsedNanos, realtimeNanos) -> { EventLogTags.writeJankCujEventsCancelRequest( cujType, unixNanos, elapsedNanos, realtimeNanos); @@ -790,16 +434,18 @@ public class InteractionJankMonitor { * @return boolean true if the tracker is cancelled successfully, false otherwise. */ @VisibleForTesting - public boolean cancel(@CujType int cujType, @Reasons int reason) { - FrameTracker tracker = getTracker(cujType); + public boolean cancel(@Cuj.CujType int cujType, @Reasons int reason) { + RunningTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. - if (tracker == null) return false; + if (tracker == null) { + return false; + } try { final TrackerResult result = new TrackerResult(); - final boolean success = tracker.getHandler().runWithScissors( - () -> result.mResult = cancelInternal(cujType, reason), EXECUTOR_TASK_TIMEOUT); + final boolean success = tracker.mConfig.getHandler().runWithScissors( + () -> result.mResult = cancelInternal(tracker, reason), EXECUTOR_TASK_TIMEOUT); if (!success) { - Log.d(TAG, "cancel failed due to timeout, CUJ=" + getNameOfCuj(cujType)); + Log.d(TAG, "cancel failed due to timeout, CUJ=" + Cuj.getNameOfCuj(cujType)); return false; } return result.mResult; @@ -810,71 +456,86 @@ public class InteractionJankMonitor { } @UiThread - private boolean cancelInternal(@CujType int cujType, @Reasons int reason) { - // remove the timeout action first. - removeTimeout(cujType); - FrameTracker tracker = getTracker(cujType); - if (tracker == null) return false; - // if the cancel call doesn't return true, another thread is handling cancel of the cuj. - if (tracker.cancel(reason)) { - removeTracker(cujType, reason); + private boolean cancelInternal(RunningTracker tracker, @Reasons int reason) { + if (removeTrackerIfCurrent(tracker, reason)) { + return false; } + tracker.mTracker.cancel(reason); return true; } @UiThread - private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) { + private RunningTracker putTrackerIfNoCurrent( + @Cuj.CujType int cuj, Supplier<RunningTracker> supplier) { synchronized (mLock) { + if (mRunningTrackers.contains(cuj)) { + return null; + } + + RunningTracker tracker = supplier.get(); + if (tracker == null) { + return null; + } + mRunningTrackers.put(cuj, tracker); if (mDebugOverlay != null) { mDebugOverlay.onTrackerAdded(cuj, tracker); } - if (DEBUG) { - Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj) - + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers)); - } + + return tracker; } } - private FrameTracker getTracker(@CujType int cuj) { + private RunningTracker getTracker(@Cuj.CujType int cuj) { synchronized (mLock) { return mRunningTrackers.get(cuj); } } + /** + * @return {@code true} if another tracker is current + */ + @UiThread + private boolean removeTrackerIfCurrent(RunningTracker tracker, int reason) { + return removeTrackerIfCurrent(tracker.mConfig.mCujType, tracker.mTracker, reason); + } + + /** + * @return {@code true} if another tracker is current + */ @UiThread - private void removeTracker(@CujType int cuj, int reason) { + private boolean removeTrackerIfCurrent(@Cuj.CujType int cuj, FrameTracker tracker, int reason) { synchronized (mLock) { + RunningTracker running = mRunningTrackers.get(cuj); + if (running == null || running.mTracker != tracker) { + return true; + } + + running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction); mRunningTrackers.remove(cuj); if (mDebugOverlay != null) { mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers); } - if (DEBUG) { - Log.d(TAG, "Removed tracker for " + getNameOfCuj(cuj) - + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers)); - } + return false; } } @WorkerThread - private void updateProperties(DeviceConfig.Properties properties) { + @VisibleForTesting + public void updateProperties(DeviceConfig.Properties properties) { for (String property : properties.getKeyset()) { switch (property) { - case SETTINGS_SAMPLING_INTERVAL_KEY: - mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL); - break; - case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY: - mTraceThresholdMissedFrames = - properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); - break; - case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY: - mTraceThresholdFrameTimeMillis = - properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); - break; - case SETTINGS_ENABLED_KEY: - mEnabled = properties.getBoolean(property, DEFAULT_ENABLED); - break; - case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY: + case SETTINGS_SAMPLING_INTERVAL_KEY -> + mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL); + case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY -> + mTraceThresholdMissedFrames = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY -> + mTraceThresholdFrameTimeMillis = + properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); + case SETTINGS_ENABLED_KEY -> + mEnabled = properties.getBoolean(property, DEFAULT_ENABLED); + case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY -> { // Never allow the debug overlay to be used on user builds boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED); @@ -885,49 +546,35 @@ public class InteractionJankMonitor { mDebugOverlay.dispose(); mDebugOverlay = null; } - break; - default: - if (DEBUG) { - Log.d(TAG, "Got a change event for an unknown property: " - + property + " => " + properties.getString(property, "")); - } + } + default -> Log.w(TAG, "Got a change event for an unknown property: " + + property + " => " + properties.getString(property, "")); } } } - @VisibleForTesting - public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() { - return mPropertiesChangedListener; - } - - /** - * Triggers the perfetto daemon to collect and upload data. - */ - @VisibleForTesting - public void trigger(Session session) { - mWorker.getThreadHandler().post( - () -> PerfettoTrigger.trigger(session.getPerfettoTrigger())); - } - /** * A helper method to translate interaction type to CUJ name. * * @param interactionType the interaction type defined in AtomsProto.java * @return the name of the interaction type + * @deprecated use {@link Cuj#getNameOfInteraction(int)} */ + @Deprecated public static String getNameOfInteraction(int interactionType) { - // There is an offset amount of 1 between cujType and interactionType. - return getNameOfCuj(getCujTypeFromInteraction(interactionType)); + return Cuj.getNameOfInteraction(interactionType); } /** - * A helper method to translate interaction type to CUJ type. + * A helper method to translate CUJ type to CUJ name. * - * @param interactionType the interaction type defined in AtomsProto.java - * @return the integer in {@link CujType} + * @param cujType the cuj type defined in this file + * @return the name of the cuj type + * @deprecated use {@link Cuj#getNameOfCuj(int)} */ - private static int getCujTypeFromInteraction(int interactionType) { - return interactionType - 1; + @Deprecated + public static String getNameOfCuj(int cujType) { + return Cuj.getNameOfCuj(cujType); } /** @@ -943,195 +590,14 @@ public class InteractionJankMonitor { mDebugYOffset = yOffset; } - /** - * A helper method for getting a string representation of all running CUJs. For example, - * "(LOCKSCREEN_TRANSITION_FROM_AOD, IME_INSETS_ANIMATION)" - */ - private static String listNamesOfCujs(SparseArray<FrameTracker> trackers) { - if (!DEBUG) { - return null; - } - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (int i = 0; i < trackers.size(); i++) { - sb.append(getNameOfCuj(trackers.keyAt(i))); - if (i < trackers.size() - 1) { - sb.append(", "); - } - } - sb.append(')'); - return sb.toString(); - } + private void postEventLogToWorkerThread(TimeFunction logFunction) { + final Instant now = Instant.now(); + final long unixNanos = TimeUnit.NANOSECONDS.convert(now.getEpochSecond(), TimeUnit.SECONDS) + + now.getNano(); + final long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + final long realtimeNanos = SystemClock.uptimeNanos(); - /** - * A helper method to translate CUJ type to CUJ name. - * - * @param cujType the cuj type defined in this file - * @return the name of the cuj type - */ - public static String getNameOfCuj(int cujType) { - // Please note: - // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME. - // 2. The returned string should be the same with the name defined in atoms.proto. - switch (cujType) { - case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE: - return "NOTIFICATION_SHADE_EXPAND_COLLAPSE"; - case CUJ_NOTIFICATION_SHADE_SCROLL_FLING: - return "NOTIFICATION_SHADE_SCROLL_FLING"; - case CUJ_NOTIFICATION_SHADE_ROW_EXPAND: - return "NOTIFICATION_SHADE_ROW_EXPAND"; - case CUJ_NOTIFICATION_SHADE_ROW_SWIPE: - return "NOTIFICATION_SHADE_ROW_SWIPE"; - case CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE: - return "NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE"; - case CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE: - return "NOTIFICATION_SHADE_QS_SCROLL_SWIPE"; - case CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS: - return "LAUNCHER_APP_LAUNCH_FROM_RECENTS"; - case CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON: - return "LAUNCHER_APP_LAUNCH_FROM_ICON"; - case CUJ_LAUNCHER_APP_CLOSE_TO_HOME: - return "LAUNCHER_APP_CLOSE_TO_HOME"; - case CUJ_LAUNCHER_APP_CLOSE_TO_PIP: - return "LAUNCHER_APP_CLOSE_TO_PIP"; - case CUJ_LAUNCHER_QUICK_SWITCH: - return "LAUNCHER_QUICK_SWITCH"; - case CUJ_NOTIFICATION_HEADS_UP_APPEAR: - return "NOTIFICATION_HEADS_UP_APPEAR"; - case CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR: - return "NOTIFICATION_HEADS_UP_DISAPPEAR"; - case CUJ_NOTIFICATION_ADD: - return "NOTIFICATION_ADD"; - case CUJ_NOTIFICATION_REMOVE: - return "NOTIFICATION_REMOVE"; - case CUJ_NOTIFICATION_APP_START: - return "NOTIFICATION_APP_START"; - case CUJ_LOCKSCREEN_PASSWORD_APPEAR: - return "LOCKSCREEN_PASSWORD_APPEAR"; - case CUJ_LOCKSCREEN_PATTERN_APPEAR: - return "LOCKSCREEN_PATTERN_APPEAR"; - case CUJ_LOCKSCREEN_PIN_APPEAR: - return "LOCKSCREEN_PIN_APPEAR"; - case CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR: - return "LOCKSCREEN_PASSWORD_DISAPPEAR"; - case CUJ_LOCKSCREEN_PATTERN_DISAPPEAR: - return "LOCKSCREEN_PATTERN_DISAPPEAR"; - case CUJ_LOCKSCREEN_PIN_DISAPPEAR: - return "LOCKSCREEN_PIN_DISAPPEAR"; - case CUJ_LOCKSCREEN_TRANSITION_FROM_AOD: - return "LOCKSCREEN_TRANSITION_FROM_AOD"; - case CUJ_LOCKSCREEN_TRANSITION_TO_AOD: - return "LOCKSCREEN_TRANSITION_TO_AOD"; - case CUJ_LAUNCHER_OPEN_ALL_APPS : - return "LAUNCHER_OPEN_ALL_APPS"; - case CUJ_LAUNCHER_ALL_APPS_SCROLL: - return "LAUNCHER_ALL_APPS_SCROLL"; - case CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET: - return "LAUNCHER_APP_LAUNCH_FROM_WIDGET"; - case CUJ_SETTINGS_PAGE_SCROLL: - return "SETTINGS_PAGE_SCROLL"; - case CUJ_LOCKSCREEN_UNLOCK_ANIMATION: - return "LOCKSCREEN_UNLOCK_ANIMATION"; - case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON: - return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON"; - case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER: - return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER"; - case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE: - return "SHADE_APP_LAUNCH_FROM_QS_TILE"; - case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON: - return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON"; - case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP: - return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP"; - case CUJ_PIP_TRANSITION: - return "PIP_TRANSITION"; - case CUJ_WALLPAPER_TRANSITION: - return "WALLPAPER_TRANSITION"; - case CUJ_USER_SWITCH: - return "USER_SWITCH"; - case CUJ_SPLASHSCREEN_AVD: - return "SPLASHSCREEN_AVD"; - case CUJ_SPLASHSCREEN_EXIT_ANIM: - return "SPLASHSCREEN_EXIT_ANIM"; - case CUJ_SCREEN_OFF: - return "SCREEN_OFF"; - case CUJ_SCREEN_OFF_SHOW_AOD: - return "SCREEN_OFF_SHOW_AOD"; - case CUJ_ONE_HANDED_ENTER_TRANSITION: - return "ONE_HANDED_ENTER_TRANSITION"; - case CUJ_ONE_HANDED_EXIT_TRANSITION: - return "ONE_HANDED_EXIT_TRANSITION"; - case CUJ_UNFOLD_ANIM: - return "UNFOLD_ANIM"; - case CUJ_SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS: - return "SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS"; - case CUJ_SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS: - return "SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS"; - case CUJ_SUW_LOADING_TO_NEXT_FLOW: - return "SUW_LOADING_TO_NEXT_FLOW"; - case CUJ_SUW_LOADING_SCREEN_FOR_STATUS: - return "SUW_LOADING_SCREEN_FOR_STATUS"; - case CUJ_SPLIT_SCREEN_ENTER: - return "SPLIT_SCREEN_ENTER"; - case CUJ_SPLIT_SCREEN_EXIT: - return "SPLIT_SCREEN_EXIT"; - case CUJ_LOCKSCREEN_LAUNCH_CAMERA: - return "LOCKSCREEN_LAUNCH_CAMERA"; - case CUJ_SPLIT_SCREEN_RESIZE: - return "SPLIT_SCREEN_RESIZE"; - case CUJ_SETTINGS_SLIDER: - return "SETTINGS_SLIDER"; - case CUJ_TAKE_SCREENSHOT: - return "TAKE_SCREENSHOT"; - case CUJ_VOLUME_CONTROL: - return "VOLUME_CONTROL"; - case CUJ_BIOMETRIC_PROMPT_TRANSITION: - return "BIOMETRIC_PROMPT_TRANSITION"; - case CUJ_SETTINGS_TOGGLE: - return "SETTINGS_TOGGLE"; - case CUJ_SHADE_DIALOG_OPEN: - return "SHADE_DIALOG_OPEN"; - case CUJ_USER_DIALOG_OPEN: - return "USER_DIALOG_OPEN"; - case CUJ_TASKBAR_EXPAND: - return "TASKBAR_EXPAND"; - case CUJ_TASKBAR_COLLAPSE: - return "TASKBAR_COLLAPSE"; - case CUJ_SHADE_CLEAR_ALL: - return "SHADE_CLEAR_ALL"; - case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION: - return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION"; - case CUJ_LOCKSCREEN_OCCLUSION: - return "LOCKSCREEN_OCCLUSION"; - case CUJ_RECENTS_SCROLLING: - return "RECENTS_SCROLLING"; - case CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS: - return "LAUNCHER_APP_SWIPE_TO_RECENTS"; - case CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE: - return "LAUNCHER_CLOSE_ALL_APPS_SWIPE"; - case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME: - return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME"; - case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION: - return "LOCKSCREEN_CLOCK_MOVE_ANIMATION"; - case CUJ_LAUNCHER_OPEN_SEARCH_RESULT: - return "LAUNCHER_OPEN_SEARCH_RESULT"; - case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK: - return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK"; - case CUJ_IME_INSETS_SHOW_ANIMATION: - return "IME_INSETS_SHOW_ANIMATION"; - case CUJ_IME_INSETS_HIDE_ANIMATION: - return "IME_INSETS_HIDE_ANIMATION"; - case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER: - return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER"; - case CUJ_LAUNCHER_UNFOLD_ANIM: - return "LAUNCHER_UNFOLD_ANIM"; - case CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY: - return "PREDICTIVE_BACK_CROSS_ACTIVITY"; - case CUJ_PREDICTIVE_BACK_CROSS_TASK: - return "PREDICTIVE_BACK_CROSS_TASK"; - case CUJ_PREDICTIVE_BACK_HOME: - return "PREDICTIVE_BACK_HOME"; - } - return "UNKNOWN"; + mWorker.post(() -> logFunction.invoke(unixNanos, elapsedNanos, realtimeNanos)); } private static class TrackerResult { @@ -1147,9 +613,10 @@ public class InteractionJankMonitor { private final Context mContext; private final long mTimeout; private final String mTag; + private final String mSessionName; private final boolean mSurfaceOnly; private final SurfaceControl mSurfaceControl; - private final @CujType int mCujType; + private final @Cuj.CujType int mCujType; private final boolean mDeferMonitor; private final Handler mHandler; @@ -1167,17 +634,17 @@ public class InteractionJankMonitor { private String mAttrTag = ""; private boolean mAttrSurfaceOnly; private SurfaceControl mAttrSurfaceControl; - private @CujType int mAttrCujType; + private final @Cuj.CujType int mAttrCujType; private boolean mAttrDeferMonitor = true; /** * Creates a builder which instruments only surface. - * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}. + * @param cuj The enum defined in {@link Cuj.CujType}. * @param context context * @param surfaceControl surface control * @return builder */ - public static Builder withSurface(@CujType int cuj, @NonNull Context context, + public static Builder withSurface(@Cuj.CujType int cuj, @NonNull Context context, @NonNull SurfaceControl surfaceControl) { return new Builder(cuj) .setContext(context) @@ -1187,16 +654,17 @@ public class InteractionJankMonitor { /** * Creates a builder which instruments both surface and view. - * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}. + * @param cuj The enum defined in {@link Cuj.CujType}. * @param view view * @return builder */ - public static Builder withView(@CujType int cuj, @NonNull View view) { - return new Builder(cuj).setView(view) + public static Builder withView(@Cuj.CujType int cuj, @NonNull View view) { + return new Builder(cuj) + .setView(view) .setContext(view.getContext()); } - private Builder(@CujType int cuj) { + private Builder(@Cuj.CujType int cuj) { mAttrCujType = cuj; } @@ -1281,11 +749,12 @@ public class InteractionJankMonitor { } } - private Configuration(@CujType int cuj, View view, String tag, long timeout, + private Configuration(@Cuj.CujType int cuj, View view, @NonNull String tag, long timeout, boolean surfaceOnly, Context context, SurfaceControl surfaceControl, boolean deferMonitor) { mCujType = cuj; mTag = tag; + mSessionName = generateSessionName(Cuj.getNameOfCuj(cuj), tag); mTimeout = timeout; mView = view; mSurfaceOnly = surfaceOnly; @@ -1298,6 +767,23 @@ public class InteractionJankMonitor { mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler(); } + @VisibleForTesting + public static String generateSessionName( + @NonNull String cujName, @NonNull String cujPostfix) { + final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix); + if (hasPostfix) { + final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length(); + if (cujPostfix.length() > remaining) { + cujPostfix = cujPostfix.substring(0, remaining - 3).concat("..."); + } + } + // The max length of the whole string should be: + // 105 with postfix, 83 without postfix + return hasPostfix + ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix) + : TextUtils.formatSimple("J<%s>", cujName); + } + private void validate() { boolean shouldThrow = false; final StringBuilder msg = new StringBuilder(); @@ -1360,10 +846,10 @@ public class InteractionJankMonitor { return mSurfaceControl; } - @VisibleForTesting /** * @return a view which is attached to the view tree. */ + @VisibleForTesting public View getView() { return mView; } @@ -1375,7 +861,7 @@ public class InteractionJankMonitor { return mDeferMonitor; } - @VisibleForTesting + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public Handler getHandler() { return mHandler; } @@ -1387,79 +873,27 @@ public class InteractionJankMonitor { public int getDisplayId() { return (mSurfaceOnly ? mContext : mView.getContext()).getDisplayId(); } - } - - /** - * A class to represent a session. - */ - public static class Session { - @CujType - private final int mCujType; - private final long mTimeStamp; - @Reasons - private int mReason = REASON_END_UNKNOWN; - private final String mName; - - public Session(@CujType int cujType, @NonNull String postfix) { - mCujType = cujType; - mTimeStamp = System.nanoTime(); - mName = generateSessionName(getNameOfCuj(cujType), postfix); - } - - private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) { - final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix); - // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME. - if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) { - throw new IllegalArgumentException(TextUtils.formatSimple( - "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME)); - } - if (hasPostfix) { - final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length(); - if (cujPostfix.length() > remaining) { - cujPostfix = cujPostfix.substring(0, remaining - 3).concat("..."); - } - } - // The max length of the whole string should be: - // 105 with postfix, 83 without postfix - return hasPostfix - ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix) - : TextUtils.formatSimple("J<%s>", cujName); - } - @CujType - public int getCuj() { - return mCujType; + public String getSessionName() { + return mSessionName; } public int getStatsdInteractionType() { - return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType]; + return Cuj.getStatsdInteractionType(mCujType); } /** Describes whether the measurement from this session should be written to statsd. */ public boolean logToStatsd() { - return getStatsdInteractionType() != NO_STATSD_LOGGING; + return Cuj.logToStatsd(mCujType); } public String getPerfettoTrigger() { - return String.format(Locale.US, "com.android.telemetry.interaction-jank-monitor-%d", - mCujType); - } - - public String getName() { - return mName; - } - - public long getTimeStamp() { - return mTimeStamp; - } - - public void setReason(@Reasons int reason) { - mReason = reason; + return TextUtils.formatSimple( + "com.android.telemetry.interaction-jank-monitor-%d", mCujType); } - @Reasons - public int getReason() { - return mReason; + public @Cuj.CujType int getCujType() { + return mCujType; } } @@ -1468,15 +902,15 @@ public class InteractionJankMonitor { void invoke(long unixNanos, long elapsedNanos, long realtimeNanos); } - private void postEventLogToWorkerThread(TimeFunction logFunction) { - final Instant now = Instant.now(); - final long unixNanos = TimeUnit.NANOSECONDS.convert(now.getEpochSecond(), TimeUnit.SECONDS) - + now.getNano(); - final long elapsedNanos = SystemClock.elapsedRealtimeNanos(); - final long realtimeNanos = SystemClock.uptimeNanos(); + static class RunningTracker { + public final Configuration mConfig; + public final FrameTracker mTracker; + public final Runnable mTimeoutAction; - mWorker.getThreadHandler().post(() -> { - logFunction.invoke(unixNanos, elapsedNanos, realtimeNanos); - }); + RunningTracker(Configuration config, FrameTracker tracker, Runnable timeoutAction) { + this.mConfig = config; + this.mTracker = tracker; + this.mTimeoutAction = timeoutAction; + } } } diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java index ef7944c21ad2..f3f16a0c662d 100644 --- a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java +++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java @@ -34,7 +34,6 @@ import android.view.WindowCallbacks; import com.android.internal.annotations.GuardedBy; import com.android.internal.jank.FrameTracker.Reasons; -import com.android.internal.jank.InteractionJankMonitor.CujType; /** * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window @@ -94,14 +93,14 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { } @UiThread - private boolean attachViewRootIfNeeded(FrameTracker tracker) { - FrameTracker.ViewRootWrapper viewRoot = tracker.getViewRoot(); + private boolean attachViewRootIfNeeded(InteractionJankMonitor.RunningTracker tracker) { + FrameTracker.ViewRootWrapper viewRoot = tracker.mTracker.getViewRoot(); if (mViewRoot == null && viewRoot != null) { // Add a trace marker so we can identify traces that were captured while the debug // overlay was enabled. Traces that use the debug overlay should NOT be used for // performance analysis. Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0); - mHandler = tracker.getHandler(); + mHandler = tracker.mConfig.getHandler(); mViewRoot = viewRoot; mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this), InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT); @@ -111,11 +110,12 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { return false; } + @GuardedBy("mLock") private float getWidthOfLongestCujName(int cujFontSize) { mDebugPaint.setTextSize(cujFontSize); float maxLength = 0; for (int i = 0; i < mRunningCujs.size(); i++) { - String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i)); + String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); float textLength = mDebugPaint.measureText(cujName); if (textLength > maxLength) { maxLength = textLength; @@ -149,8 +149,8 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { } @UiThread - void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason, - SparseArray<FrameTracker> runningTrackers) { + void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason, + SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) { synchronized (mLock) { mRunningCujs.put(removedCuj, reason); // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended @@ -164,7 +164,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { // trackers for (int i = 0; i < runningTrackers.size(); i++) { if (mViewRoot.equals( - runningTrackers.valueAt(i).getViewRoot())) { + runningTrackers.valueAt(i).mTracker.getViewRoot())) { needsNewViewRoot = false; break; } @@ -185,7 +185,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { } @UiThread - void onTrackerAdded(@CujType int addedCuj, FrameTracker tracker) { + void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) { synchronized (mLock) { // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ // is still running @@ -230,41 +230,44 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks { int cujFontSize = dipToPx(18); final float cujNameTextHeight = getTextHeight(cujFontSize); final float packageNameTextHeight = getTextHeight(packageNameFontSize); - float maxLength = getWidthOfLongestCujName(cujFontSize); - final int dx = (int) ((w - maxLength) / 2f); - canvas.translate(dx, dy); - // Draw background rectangle for displaying the text showing the CUJ name - mDebugPaint.setColor(mBgColor); - canvas.drawRect( - -padding * 2, // more padding on top so we can draw the package name - -padding, - padding * 2 + maxLength, - padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(), - mDebugPaint); - mDebugPaint.setTextSize(packageNameFontSize); - mDebugPaint.setColor(Color.BLACK); - mDebugPaint.setStrikeThruText(false); - canvas.translate(0, packageNameTextHeight); - canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint); - mDebugPaint.setTextSize(cujFontSize); - // Draw text for CUJ names - for (int i = 0; i < mRunningCujs.size(); i++) { - int status = mRunningCujs.valueAt(i); - if (status == REASON_STILL_RUNNING) { - mDebugPaint.setColor(Color.BLACK); - mDebugPaint.setStrikeThruText(false); - } else if (status == REASON_END_NORMAL) { - mDebugPaint.setColor(Color.GRAY); - mDebugPaint.setStrikeThruText(false); - } else { - // Cancelled, or otherwise ended for a bad reason - mDebugPaint.setColor(Color.RED); - mDebugPaint.setStrikeThruText(true); + synchronized (mLock) { + float maxLength = getWidthOfLongestCujName(cujFontSize); + + final int dx = (int) ((w - maxLength) / 2f); + canvas.translate(dx, dy); + // Draw background rectangle for displaying the text showing the CUJ name + mDebugPaint.setColor(mBgColor); + canvas.drawRect( + -padding * 2, // more padding on top so we can draw the package name + -padding, + padding * 2 + maxLength, + padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(), + mDebugPaint); + mDebugPaint.setTextSize(packageNameFontSize); + mDebugPaint.setColor(Color.BLACK); + mDebugPaint.setStrikeThruText(false); + canvas.translate(0, packageNameTextHeight); + canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint); + mDebugPaint.setTextSize(cujFontSize); + // Draw text for CUJ names + for (int i = 0; i < mRunningCujs.size(); i++) { + int status = mRunningCujs.valueAt(i); + if (status == REASON_STILL_RUNNING) { + mDebugPaint.setColor(Color.BLACK); + mDebugPaint.setStrikeThruText(false); + } else if (status == REASON_END_NORMAL) { + mDebugPaint.setColor(Color.GRAY); + mDebugPaint.setStrikeThruText(false); + } else { + // Cancelled, or otherwise ended for a bad reason + mDebugPaint.setColor(Color.RED); + mDebugPaint.setStrikeThruText(true); + } + String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i)); + canvas.translate(0, cujNameTextHeight); + canvas.drawText(cujName, 0, 0, mDebugPaint); } - String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i)); - canvas.translate(0, cujNameTextHeight); - canvas.drawText(cujName, 0, 0, mDebugPaint); } } } diff --git a/core/java/com/android/internal/net/TEST_MAPPING b/core/java/com/android/internal/net/TEST_MAPPING new file mode 100644 index 000000000000..971ad36eecba --- /dev/null +++ b/core/java/com/android/internal/net/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "postsubmit": [ + { + "name": "FrameworksNetTests", + "options": [ + { + "exclude-annotation": "com.android.testutils.SkipPresubmit" + } + ] + } + ] +} diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index 1f44b338f3f7..ed943cb2385d 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -55,15 +55,20 @@ import java.util.concurrent.atomic.AtomicReference; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass +@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.hoststubgen.nativesubstitution.LongArrayMultiStateCounter_host") public final class LongArrayMultiStateCounter implements Parcelable { /** * Container for a native equivalent of a long[]. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass + @android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( + "com.android.hoststubgen.nativesubstitution" + + ".LongArrayMultiStateCounter_host$LongArrayContainer_host") public static class LongArrayContainer { - private static final NativeAllocationRegistry sRegistry = - NativeAllocationRegistry.createMalloced( - LongArrayContainer.class.getClassLoader(), native_getReleaseFunc()); + private static NativeAllocationRegistry sRegistry; // Visible to other objects in this package so that it can be passed to @CriticalNative // methods. @@ -73,9 +78,26 @@ public final class LongArrayMultiStateCounter implements Parcelable { public LongArrayContainer(int length) { mLength = length; mNativeObject = native_init(length); + registerNativeAllocation(); + } + + @android.ravenwood.annotation.RavenwoodReplace + private void registerNativeAllocation() { + if (sRegistry == null) { + synchronized (LongArrayMultiStateCounter.class) { + if (sRegistry == null) { + sRegistry = NativeAllocationRegistry.createMalloced( + LongArrayContainer.class.getClassLoader(), native_getReleaseFunc()); + } + } + } sRegistry.registerNativeAllocation(this, mNativeObject); } + private void registerNativeAllocation$ravenwood() { + // No-op under ravenwood + } + /** * Copies the supplied values into the underlying native array. */ @@ -124,19 +146,17 @@ public final class LongArrayMultiStateCounter implements Parcelable { private static native long native_getReleaseFunc(); @FastNative - private native void native_setValues(long nativeObject, long[] array); + private static native void native_setValues(long nativeObject, long[] array); @FastNative - private native void native_getValues(long nativeObject, long[] array); + private static native void native_getValues(long nativeObject, long[] array); @FastNative - private native boolean native_combineValues(long nativeObject, long[] array, + private static native boolean native_combineValues(long nativeObject, long[] array, int[] indexMap); } - private static final NativeAllocationRegistry sRegistry = - NativeAllocationRegistry.createMalloced( - LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc()); + private static volatile NativeAllocationRegistry sRegistry; private static final AtomicReference<LongArrayContainer> sTmpArrayContainer = new AtomicReference<>(); @@ -152,12 +172,30 @@ public final class LongArrayMultiStateCounter implements Parcelable { mStateCount = stateCount; mLength = arrayLength; mNativeObject = native_init(stateCount, arrayLength); + registerNativeAllocation(); + } + + @android.ravenwood.annotation.RavenwoodReplace + private void registerNativeAllocation() { + if (sRegistry == null) { + synchronized (LongArrayMultiStateCounter.class) { + if (sRegistry == null) { + sRegistry = NativeAllocationRegistry.createMalloced( + LongArrayMultiStateCounter.class.getClassLoader(), + native_getReleaseFunc()); + } + } + } sRegistry.registerNativeAllocation(this, mNativeObject); } + private void registerNativeAllocation$ravenwood() { + // No-op under ravenwood + } + private LongArrayMultiStateCounter(Parcel in) { mNativeObject = native_initFromParcel(in); - sRegistry.registerNativeAllocation(this, mNativeObject); + registerNativeAllocation(); mStateCount = native_getStateCount(mNativeObject); mLength = native_getArrayLength(mNativeObject); @@ -361,10 +399,10 @@ public final class LongArrayMultiStateCounter implements Parcelable { long longArrayContainerNativeObject, int state); @FastNative - private native String native_toString(long nativeObject); + private static native String native_toString(long nativeObject); @FastNative - private native void native_writeToParcel(long nativeObject, Parcel dest, int flags); + private static native void native_writeToParcel(long nativeObject, Parcel dest, int flags); @FastNative private static native long native_initFromParcel(Parcel parcel); diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java index b12df6ed36ca..4701af3f2ddc 100644 --- a/core/java/com/android/internal/os/SomeArgs.java +++ b/core/java/com/android/internal/os/SomeArgs.java @@ -26,6 +26,7 @@ import android.os.Build; * it is responsibility of the client to recycle and instance * once it is no longer used. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class SomeArgs { private static final int MAX_POOL_SIZE = 10; diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java index 4ed361f24439..6c09b7c04fa7 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java @@ -112,7 +112,7 @@ public interface ParsingPackage { ParsingPackage addUsesOptionalNativeLibrary(String libraryName); ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor, - String[] certSha256Digests); + String[] certSha256Digests, boolean usesSdkLibrariesOptional); ParsingPackage addUsesStaticLibrary(String libraryName, long version, String[] certSha256Digests); diff --git a/core/java/com/android/internal/policy/KeyInterceptionInfo.java b/core/java/com/android/internal/policy/KeyInterceptionInfo.java index 964be01952ea..b20f6d225b69 100644 --- a/core/java/com/android/internal/policy/KeyInterceptionInfo.java +++ b/core/java/com/android/internal/policy/KeyInterceptionInfo.java @@ -26,10 +26,12 @@ public class KeyInterceptionInfo { public final int layoutParamsPrivateFlags; // Debug friendly name to help identify the window public final String windowTitle; + public final int windowOwnerUid; - public KeyInterceptionInfo(int type, int flags, String title) { + public KeyInterceptionInfo(int type, int flags, String title, int uid) { layoutParamsType = type; layoutParamsPrivateFlags = flags; windowTitle = title; + windowOwnerUid = uid; } } diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index 4bb7c33b41e2..8c2a52560050 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -93,6 +93,7 @@ public enum ProtoLogGroup implements IProtoLogGroup { WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM), WM_DEBUG_DIMMER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), + WM_DEBUG_TPL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), TEST_GROUP(true, true, false, "WindowManagerProtoLogTest"); private final boolean mEnabled; diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java index 154ea52bf9ba..d928bbc3e9ad 100644 --- a/core/java/com/android/internal/util/BitUtils.java +++ b/core/java/com/android/internal/util/BitUtils.java @@ -31,6 +31,7 @@ import java.util.function.IntFunction; * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations. * {@hide} */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class BitUtils { private BitUtils() {} diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java index ffae3ce89ce4..91f89f39fc4d 100644 --- a/core/java/com/android/internal/util/BitwiseInputStream.java +++ b/core/java/com/android/internal/util/BitwiseInputStream.java @@ -26,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage; * * NOTE -- This class is not threadsafe. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class BitwiseInputStream { // The byte array being read from. diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java index 9f4150860887..9b7568b54639 100644 --- a/core/java/com/android/internal/util/BitwiseOutputStream.java +++ b/core/java/com/android/internal/util/BitwiseOutputStream.java @@ -26,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage; * * NOTE -- This class is not threadsafe. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class BitwiseOutputStream { // The byte array being written to, which will be grown as needed. diff --git a/core/java/com/android/internal/util/CallbackRegistry.java b/core/java/com/android/internal/util/CallbackRegistry.java index 0f228d4775fd..0ae8e1d1e0c4 100644 --- a/core/java/com/android/internal/util/CallbackRegistry.java +++ b/core/java/com/android/internal/util/CallbackRegistry.java @@ -39,6 +39,7 @@ import java.util.List; * @param <T> The notification sender type. Typically this is the containing class. * @param <A> Opaque argument used to pass additional data beyond an int. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class CallbackRegistry<C, T, A> implements Cloneable { private static final String TAG = "CallbackRegistry"; diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java index 8fe2b9cdf1e5..d1ff034ff001 100644 --- a/core/java/com/android/internal/util/DumpUtils.java +++ b/core/java/com/android/internal/util/DumpUtils.java @@ -37,6 +37,7 @@ import java.util.function.Predicate; * Test: atest FrameworksCoreTests:DumpUtilsTest */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class DumpUtils { /** @@ -92,6 +93,8 @@ public final class DumpUtils { * @return true if access should be granted. * @hide */ + @android.ravenwood.annotation.RavenwoodThrow( + blockedBy = android.permission.PermissionManager.class) public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) { if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -112,6 +115,8 @@ public final class DumpUtils { * @return true if access should be granted. * @hide */ + @android.ravenwood.annotation.RavenwoodThrow( + blockedBy = android.permission.PermissionManager.class) public static boolean checkUsageStatsPermission(Context context, String tag, PrintWriter pw) { // System internals always get access final int uid = Binder.getCallingUid(); @@ -166,6 +171,8 @@ public final class DumpUtils { * @return true if access should be granted. * @hide */ + @android.ravenwood.annotation.RavenwoodThrow( + blockedBy = android.permission.PermissionManager.class) public static boolean checkDumpAndUsageStatsPermission(Context context, String tag, PrintWriter pw) { return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw); diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java index b7dbee53a4be..6e8fef281445 100644 --- a/core/java/com/android/internal/util/FastMath.java +++ b/core/java/com/android/internal/util/FastMath.java @@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage; /** * Fast and loose math routines. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class FastMath { /** diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java index 63124def6d2f..fcd6df091c88 100644 --- a/core/java/com/android/internal/util/FastPrintWriter.java +++ b/core/java/com/android/internal/util/FastPrintWriter.java @@ -32,6 +32,7 @@ import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class FastPrintWriter extends PrintWriter { private static class DummyWriter extends Writer { @Override diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java index 8c12e36a27d1..83d06d180179 100644 --- a/core/java/com/android/internal/util/GrowingArrayUtils.java +++ b/core/java/com/android/internal/util/GrowingArrayUtils.java @@ -29,6 +29,7 @@ import android.os.Build; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class GrowingArrayUtils { /** diff --git a/core/java/com/android/internal/util/HeavyHitterSketch.java b/core/java/com/android/internal/util/HeavyHitterSketch.java index e18acaf36797..2973d305f517 100644 --- a/core/java/com/android/internal/util/HeavyHitterSketch.java +++ b/core/java/com/android/internal/util/HeavyHitterSketch.java @@ -35,6 +35,7 @@ import java.util.List; * <p> * {@hide} */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public interface HeavyHitterSketch<T> { /** * Return the default implementation. diff --git a/core/java/com/android/internal/util/LineBreakBufferedWriter.java b/core/java/com/android/internal/util/LineBreakBufferedWriter.java index 552a93f6666a..8393b229e011 100644 --- a/core/java/com/android/internal/util/LineBreakBufferedWriter.java +++ b/core/java/com/android/internal/util/LineBreakBufferedWriter.java @@ -27,6 +27,7 @@ import java.util.Arrays; * * Note: this class is not thread-safe. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class LineBreakBufferedWriter extends PrintWriter { /** diff --git a/core/java/com/android/internal/util/ObjectUtils.java b/core/java/com/android/internal/util/ObjectUtils.java index 0e7b93d406f5..292ee14911c5 100644 --- a/core/java/com/android/internal/util/ObjectUtils.java +++ b/core/java/com/android/internal/util/ObjectUtils.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import java.util.Objects; /** @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ObjectUtils { private ObjectUtils() {} diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java index 3147c34a7129..70c3869d8e23 100644 --- a/core/java/com/android/internal/util/Parcelling.java +++ b/core/java/com/android/internal/util/Parcelling.java @@ -38,6 +38,7 @@ import java.util.regex.Pattern; * * @param <T> the type being [un]parcelled */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public interface Parcelling<T> { /** diff --git a/core/java/com/android/internal/util/ParseUtils.java b/core/java/com/android/internal/util/ParseUtils.java index a591f4aa41fc..a16ce2b0570f 100644 --- a/core/java/com/android/internal/util/ParseUtils.java +++ b/core/java/com/android/internal/util/ParseUtils.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; * Test: atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class ParseUtils { private ParseUtils() { } diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java index b726d5d75239..6cf241e65d00 100644 --- a/core/java/com/android/internal/util/ProcFileReader.java +++ b/core/java/com/android/internal/util/ProcFileReader.java @@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets; * Currently doesn't support formats based on {@code \0}, tabs. * Consecutive spaces are treated as a single delimiter. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ProcFileReader implements Closeable { private final InputStream mStream; private final byte[] mBuffer; diff --git a/core/java/com/android/internal/util/ProgressReporter.java b/core/java/com/android/internal/util/ProgressReporter.java index 7a8efba8a637..5640d8f28330 100644 --- a/core/java/com/android/internal/util/ProgressReporter.java +++ b/core/java/com/android/internal/util/ProgressReporter.java @@ -25,6 +25,7 @@ import android.os.RemoteException; import android.util.MathUtils; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; /** * Tracks and reports progress of a single task to a {@link IProgressListener}. @@ -175,7 +176,8 @@ public class ProgressReporter { } } - int getProgress() { + @VisibleForTesting + public int getProgress() { return mProgress; } diff --git a/core/java/com/android/internal/util/QuickSelect.java b/core/java/com/android/internal/util/QuickSelect.java index 17739c9c8832..052efede2fba 100644 --- a/core/java/com/android/internal/util/QuickSelect.java +++ b/core/java/com/android/internal/util/QuickSelect.java @@ -28,6 +28,7 @@ import java.util.List; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class QuickSelect { private static <T> int selectImpl(@NonNull List<T> list, int left, int right, int k, @NonNull Comparator<? super T> comparator) { diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java index 8fc4c30e54ab..04886598de35 100644 --- a/core/java/com/android/internal/util/RingBuffer.java +++ b/core/java/com/android/internal/util/RingBuffer.java @@ -27,6 +27,7 @@ import java.util.Arrays; * full, oldest events are dropped when new events are added. * {@hide} */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class RingBuffer<T> { // Array for storing events. diff --git a/core/java/com/android/internal/util/StringPool.java b/core/java/com/android/internal/util/StringPool.java index c5180a3fe8cf..94f6ec9677ec 100644 --- a/core/java/com/android/internal/util/StringPool.java +++ b/core/java/com/android/internal/util/StringPool.java @@ -23,6 +23,7 @@ package com.android.internal.util; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class StringPool { private final String[] mPool = new String[512]; diff --git a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java index ccec6c61b92a..bb5224f65904 100644 --- a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java +++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java @@ -32,6 +32,7 @@ import java.util.Objects; * * @hide */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class DumpableContainerImpl implements DumpableContainer { private static final String TAG = DumpableContainerImpl.class.getSimpleName(); diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java index 4e4f26cd6ad6..f86595f1ea7b 100644 --- a/core/java/com/android/server/pm/pkg/AndroidPackage.java +++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java @@ -1340,6 +1340,15 @@ public interface AndroidPackage { @Nullable long[] getUsesSdkLibrariesVersionsMajor(); + + /** + * @see R.styleable#AndroidManifestUsesSdkLibrary_optional + * @hide + */ + @Immutable.Ignore + @Nullable + boolean[] getUsesSdkLibrariesOptional(); + /** * TODO(b/135203078): Move static library stuff to an inner data class * diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java index 585847da566c..392b87e6bd04 100644 --- a/core/java/com/google/android/collect/Lists.java +++ b/core/java/com/google/android/collect/Lists.java @@ -25,6 +25,7 @@ import java.util.Collections; * Provides static methods for creating {@code List} instances easily, and other * utility methods for working with lists. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class Lists { /** diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java index cd4c1280545e..6492b0350669 100644 --- a/core/java/com/google/android/collect/Maps.java +++ b/core/java/com/google/android/collect/Maps.java @@ -24,6 +24,7 @@ import java.util.HashMap; /** * Provides static methods for creating mutable {@code Maps} instances easily. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class Maps { /** * Creates a {@code HashMap} instance. diff --git a/core/java/com/google/android/collect/Sets.java b/core/java/com/google/android/collect/Sets.java index e2429157d529..76eaf21cae04 100644 --- a/core/java/com/google/android/collect/Sets.java +++ b/core/java/com/google/android/collect/Sets.java @@ -31,6 +31,7 @@ import java.util.TreeSet; * other static methods for working with Sets. * */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class Sets { /** diff --git a/core/jni/Android.bp b/core/jni/Android.bp index f365dbb1d46a..2a744e343ccd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -49,8 +49,6 @@ cc_library_shared_for_libandroid_runtime { "-Wno-unused-parameter", "-Wunused", "-Wunreachable-code", - - "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash", ], cppflags: ["-Wno-conversion-null"], @@ -284,8 +282,6 @@ cc_library_shared_for_libandroid_runtime { "libscrypt_static", "libstatssocket_lazy", "libskia", - "libtextclassifier_hash_static", - "libexpresslog_jni", ], shared_libs: [ @@ -372,7 +368,6 @@ cc_library_shared_for_libandroid_runtime { "bionic_libc_platform_headers", "dnsproxyd_protocol_headers", "flatbuffer_headers", - "libtextclassifier_hash_headers", "tensorflow_headers", ], runtime_libs: [ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 50253cf9e457..c24d21dda68c 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -201,7 +201,6 @@ extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env); -extern int register_com_android_modules_expresslog_Utils(JNIEnv* env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); @@ -1590,7 +1589,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_incremental_IncrementalManager), REG_JNI(register_com_android_internal_content_om_OverlayConfig), REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl), - REG_JNI(register_com_android_modules_expresslog_Utils), REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter), diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index e0bcef642d82..de1ce4e29198 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -1014,6 +1014,10 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz) return cfg_state == CONFIG_SET; } +static jboolean android_os_Debug_logAllocatorStats(JNIEnv*, jobject) { + return mallopt(M_LOG_STATS, 0) == 1 ? JNI_TRUE : JNI_FALSE; +} + /* * JNI registration. */ @@ -1056,6 +1060,7 @@ static const JNINativeMethod gMethods[] = { {"getDmabufHeapPoolsSizeKb", "()J", (void*)android_os_Debug_getDmabufHeapPoolsSizeKb}, {"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb}, {"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack}, + {"logAllocatorStats", "()Z", (void*)android_os_Debug_logAllocatorStats}, }; int register_android_os_Debug(JNIEnv *env) diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp index dab47e9a9e28..76b05eac82af 100644 --- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp +++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp @@ -107,7 +107,7 @@ static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, *vector = counter->getCount(state); } -static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) { +static jobject native_toString(JNIEnv *env, jclass, jlong nativePtr) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); return env->NewStringUTF(counter->toString().c_str()); @@ -127,7 +127,7 @@ static void throwWriteRE(JNIEnv *env, binder_status_t status) { } \ } -static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel, +static void native_writeToParcel(JNIEnv *env, jclass, jlong nativePtr, jobject jParcel, jint flags) { battery::LongArrayMultiStateCounter *counter = reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr); @@ -161,7 +161,7 @@ static void throwReadException(JNIEnv *env, binder_status_t status) { } \ } -static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) { +static jlong native_initFromParcel(JNIEnv *env, jclass, jobject jParcel) { ndk::ScopedAParcel parcel(AParcel_fromJavaParcel(env, jParcel)); int32_t stateCount; @@ -253,7 +253,7 @@ static jlong native_getReleaseFunc_LongArrayContainer() { return reinterpret_cast<jlong>(native_dispose_LongArrayContainer); } -static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, +static void native_setValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRO scopedArray(env, jarray); @@ -264,7 +264,7 @@ static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong std::copy(array, array + size, vector->data()); } -static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, +static void native_getValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr, jlongArray jarray) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRW scopedArray(env, jarray); @@ -273,7 +273,7 @@ static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get()); } -static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr, +static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jclass, jlong nativePtr, jlongArray jarray, jintArray jindexMap) { std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr); ScopedLongArrayRW scopedArray(env, jarray); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c55b8089242a..dd93586c340b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7832,6 +7832,22 @@ <permission android:name="android.permission.PREPARE_FACTORY_RESET" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows focused window to override the default behavior of supported system keys. + The following keycodes are supported: + <p> KEYCODE_STEM_PRIMARY + <p>If an app is granted this permission and has a focused window, it will be allowed to + receive supported key events that are otherwise handled by the system. The app can choose + to consume the key events and trigger its own behavior, in which case the default key + behavior will be skipped. + <p>For example, KEYCODE_STEM_PRIMARY by default opens recent app launcher. If the foreground + fitness app is granted this permission, it can repurpose the KEYCODE_STEM_PRIMARY button + to pause/resume the current fitness session. + <p>Protection level: signature|privileged + @FlaggedApi("com.android.input.flags.override_key_behavior_permission_apis") + @hide --> + <permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" + android:protectionLevel="signature|privileged" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 625b1413e403..356c5e10f729 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Sleutelborduitleg is gestel op <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> … Tik om dit te verander."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fisieke sleutelborde is opgestel"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tik om sleutelborde te bekyk"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privaat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Kloon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Werk"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Werk 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Toets"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string> </resources> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 04fb30076217..8af993f06243 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2363,18 +2363,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"تم ضبط تنسيق لوحة المفاتيح على <xliff:g id="LAYOUT_1">%1$s</xliff:g> و<xliff:g id="LAYOUT_2">%2$s</xliff:g> و<xliff:g id="LAYOUT_3">%3$s</xliff:g>… انقر لتغييره."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"تم إعداد لوحات المفاتيح الخارجية"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"انقر لعرض لوحات المفاتيح."</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ملف شخصي خاص"</string> + <string name="profile_label_clone" msgid="769106052210954285">"نسخة طبق الأصل"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ملف العمل"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ملف العمل 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string> </resources> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 826844f4321b..5e3d0f6b8546 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীব’ৰ্ডৰ লে’আউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> হিচাপে ছেট কৰা হৈছে… সলনি কৰিবলৈ টিপক।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ভৌতিক কীব’ৰ্ড কনফিগাৰ কৰা হৈছে"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীব’ৰ্ড চাবলৈ টিপক"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ক্ল’ন"</string> + <string name="profile_label_work" msgid="3495359133038584618">"কৰ্মস্থান"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"কৰ্মস্থান ২"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"কৰ্মস্থান ৩"</string> + <string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string> </resources> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 66a5670ff178..a000117c8f7b 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatura düzəni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> kimi ayarlanıb… Dəyişmək üçün toxunun."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziki klaviaturalar konfiqurasiya edilib"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klaviaturalara baxmaq üçün toxunun"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Şəxsi"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"İş"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string> </resources> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index b6121edacfaf..080d1bddaa1a 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Raspored tastature je podešen na <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Dodirnite da biste promenili."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fizičke tastature su konfigurisane"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Dodirnite da biste videli tastature"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privatno"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klonirano"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Posao"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Posao 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string> </resources> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 3c01f1b4a2fe..1cca6ff09928 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Раскладка клавіятуры наладжана для наступных моў: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Націсніце, каб змяніць."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Фізічныя клавіятуры наладжаны"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Націсніце, каб праглядзець клавіятуры"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Прыватны"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Працоўны"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Працоўны 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Працоўны 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string> </resources> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 3b1ff9e075f4..8f994cacdef0 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"За клавиатурната подредба са зададени <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> и <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Докоснете за промяна."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физическите клавиатури са конфигурирани"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Докоснете за преглед на клавиатурите"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Частни"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клониране"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Служебни"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Служебни 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Служебни 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string> </resources> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 58dd7c2165ad..f85df4050476 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীবোর্ড লেআউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>-এ সেট করা আছে… পালটাতে ট্যাপ করুন।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ফিজিক্যাল কীবোর্ড কনফিগার করা হয়েছে"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীবোর্ড দেখতে ট্যাপ করুন"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ক্লোন করুন"</string> + <string name="profile_label_work" msgid="3495359133038584618">"অফিস"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"২য় অফিস"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"৩য় অফিস"</string> + <string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string> </resources> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index aa66e02e8c97..5d207a8c456e 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Raspored tastature je postavljen na <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Dodirnite da promijenite."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fizičke tastature su konfigurirane"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Dodirnite da pregledate tastature"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privatno"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Poslovno"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"2. poslovno"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Testno"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string> </resources> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index be8654db08f6..cf3a8ade3a73 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Disseny del teclat definit en <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toca per canviar-ho."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclats físic configurats"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toca per veure els teclats"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Treball"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Treball 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Prova"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 35def43ac231..0d586e472959 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Tastaturlayoutet er angivet som <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tryk for at ændre dette."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fysiske tastaturer er konfigureret"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tryk for at se tastaturer"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Arbejde"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Arbejde 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string> </resources> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 179bdb435420..6c47dd674316 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Keyboard layout set to <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tap to change."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Physical keyboards configured"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tap to view keyboards"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Private"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Work"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Work 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 2a4f88e52b10..5c26afa9903d 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Keyboard layout set to <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tap to change."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Physical keyboards configured"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tap to view keyboards"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Private"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Work"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Work 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> </resources> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 0f08d2fb7fd8..80da8b947742 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Keyboard layout set to <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tap to change."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Physical keyboards configured"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tap to view keyboards"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Private"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Work"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Work 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 29f0f8f7623a..051528d5b5b0 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Diseño del teclado definido como <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toca para cambiarlo."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toca para ver los teclados"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privado"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Trabajo"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Trabajo 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Común"</string> </resources> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 4a42b0066a96..f44b8a4bbfe1 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatuuripaigutuseks on määratud <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> … puudutage muutmiseks."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Füüsilised klaviatuurid on seadistatud"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Puudutage klaviatuuride vaatamiseks"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privaatne"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Kloon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Töö"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Töö 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string> </resources> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 3b76e8886613..8bf5d972f955 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Ezarri da <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> eta <xliff:g id="LAYOUT_3">%3$s</xliff:g> gisa teklatuaren diseinua… Diseinu hori aldatzeko, sakatu hau."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Konfiguratu dira teklatu fisikoak"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Sakatu hau teklatuak ikusteko"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Pribatua"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klona"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Lanekoa"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Lanekoa 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string> </resources> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 71dbc43bb4ec..c1b67ad5c696 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"جانمایی صفحهکلید چنین تنظیم شد: <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g>… برای تغییر ضربه بزنید"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"صفحهکلیدهای فیزیکی پیکربندی شدند"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"برای مشاهده صفحهکلیدها ضربه بزنید"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"خصوصی"</string> + <string name="profile_label_clone" msgid="769106052210954285">"مشابهسازی"</string> + <string name="profile_label_work" msgid="3495359133038584618">"کار"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"کار ۲"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"کار ۳"</string> + <string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string> </resources> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index c69bafebcf86..8c643458c90d 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Näppäimistöasetteluksi valittu <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Muuta asetuksia napauttamalla."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyysiset näppäimistöt määritetty"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Katso näppäimistöt napauttamalla"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Yksityinen"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Kloonaus"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Työ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Työ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Testi"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string> </resources> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 6c88f456d1b7..177fd1330378 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Disposition du clavier définie à <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Touchez pour modifier."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Claviers physiques configurés"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Touchez pour afficher les claviers"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privé"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Professionnel"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Professionnel 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string> </resources> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index c1e2bbfe51ad..6600c2941a61 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"O deseño do teclado estableceuse en <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toca para cambialo."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Configuráronse varios teclados físicos"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toca para ver os teclados"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privado"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clonado"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Traballo"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Traballo 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Proba"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string> </resources> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index da75282afe5b..d8478695d785 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"કીબોર્ડનું લેઆઉટ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> પર સેટ કરવામાં આવ્યું છે… બદલવા માટે ટૅપ કરો."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ભૌતિક કીબોર્ડની ગોઠવણી કરવામાં આવી છે"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"કીબોર્ડ જોવા માટે ટૅપ કરો"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ખાનગી"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ક્લોન કરો"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ઑફિસ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ઑફિસ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ઑફિસ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"પરીક્ષણ કરો"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"કૉમ્યુનલ"</string> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index d448b51a4c18..aaa4c5221587 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"कीबोर्ड का लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… पर सेट कर दिया गया है. इसे बदलने के लिए टैप करें."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"फ़िज़िकल कीबोर्ड कॉन्फ़िगर किए गए"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"कीबोर्ड देखने के लिए टैप करें"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"निजी"</string> + <string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ऑफ़िस"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ऑफ़िस 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफ़िस 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string> </resources> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 5ee152741691..0e82b26e6b71 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Raspored tipkovnice postavljen je na <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Dodirnite da biste ga promijenili."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fizičke su tipkovnice konfigurirane"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Dodirnite da bi se prikazale tipkovnice"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privatno"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Kloniranje"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Posao"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Posao 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string> </resources> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 2c4c77e30301..f2236503e7d0 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"A billentyűzetkiosztás a következőkre van beállítva: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… A módosításhoz koppintson."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fizikai billentyűzetek beállítva"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Koppintson a billentyűzetek megtekintéséhez"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privát"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klón"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Munkahelyi"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"2. munkahelyi"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string> </resources> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 482e0751253e..ca227df45212 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Ստեղնաշարի համար կարգավորված են <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> դասավորությունները։ Հպեք փոխելու համար։"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Ֆիզիկական ստեղնաշարերը կարգավորված են"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Հպեք՝ ստեղնաշարերը դիտելու համար"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Անձնական"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Կլոն"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Աշխատանքային"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Աշխատանքային 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Աշխատանքային 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string> </resources> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index f18d83459294..86a0858a497e 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Tata letak keyboard disetel ke <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Ketuk untuk mengubah."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Keyboard fisik telah dikonfigurasi"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Ketuk untuk melihat keyboard"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Pribadi"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Kerja"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Kerja 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 076c5a37fddb..1cc244ff6ed0 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Layout tastiera impostato su <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tocca per cambiare l\'impostazione."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Tastiere fisiche configurate"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tocca per visualizzare le tastiere"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privato"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Lavoro"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Lavoro 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Lavoro 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Condiviso"</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 8931518f5ef0..390f6f1e686c 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"キーボードのレイアウトは<xliff:g id="LAYOUT_1">%1$s</xliff:g>、<xliff:g id="LAYOUT_2">%2$s</xliff:g>、<xliff:g id="LAYOUT_3">%3$s</xliff:g>などに設定されています。タップで変更できます。"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"物理キーボードの設定完了"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"タップするとキーボードを表示できます"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"非公開"</string> + <string name="profile_label_clone" msgid="769106052210954285">"複製"</string> + <string name="profile_label_work" msgid="3495359133038584618">"仕事用"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"仕事用 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"仕事用 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"テスト"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> </resources> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 3e33115d32ef..8eab926cf376 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2329,13 +2329,13 @@ <string name="system_locale_title" msgid="711882686834677268">"Жүйенің әдепкі параметрі"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g>-КАРТА"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Сағаттарды басқаруға арналған қосымша сағат профилінің рұқсаты"</string> - <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Қосымша қолданбаға сағаттарды басқаруға рұқсат беріледі."</string> + <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Серік қолданбаға сағаттарды басқаруға рұқсат беріледі."</string> <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Серіктес құрылғының бар-жоғын бақылау"</string> - <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Қосымша қолданбаға қолданбалар маңайда немесе алыста болған кезде қосымша құрылғының бар болуын бақылауға рұқсат беріледі."</string> + <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Серік қолданбаға құрылғылар маңайда немесе алыста болуына қарамастан серік құрылғыны табуға рұқсат беріледі."</string> <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"Ілеспе хабарлар жеткізу"</string> - <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Қосымша қолданбаға ілеспе хабарларды басқа құрылғыларға жеткізуге рұқсат беріледі."</string> + <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Серік қолданбаға басқа құрылғыларға серік хабарлар жеткізуге рұқсат беріледі."</string> <string name="permlab_startForegroundServicesFromBackground" msgid="6363004936218638382">"Экрандық режимдегі қызметтерді фоннан іске қосу"</string> - <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Қосымша қолданбаға экрандық режимдегі қызметтерді фоннан іске қосуға рұқсат беріледі."</string> + <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Серік қолданбаға экрандық режимдегі қызметтерді фоннан іске қосуға рұқсат беріледі."</string> <string name="mic_access_on_toast" msgid="2666925317663845156">"Микрофон қолжетімді."</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"Микрофон блокталған."</string> <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"Дисплейге көшірмені көрсету мүмкін емес"</string> @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Пернетақта схемасы \"<xliff:g id="LAYOUT_1">%1$s</xliff:g>\", \"<xliff:g id="LAYOUT_2">%2$s</xliff:g>\", \"<xliff:g id="LAYOUT_3">%3$s</xliff:g>\" деп орнатылды… Өзгерту үшін түртіңіз."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физикалық пернетақталар конфигурацияланды"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Пернетақталарды көру үшін түртіңіз."</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Жеке"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Жұмыс"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Жұмыс 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Жұмыс 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string> </resources> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 0bcf992af0c7..c677a3da30f0 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"បានកំណត់ប្លង់ក្ដារចុចទៅ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… សូមចុចដើម្បីប្ដូរ។"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"បានកំណត់រចនាសម្ព័ន្ធក្ដារចុចរូបវន្ត"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ចុចដើម្បីមើលក្ដារចុច"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ឯកជន"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ក្លូន"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ការងារ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ការងារទី 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ការងារទី 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ការធ្វើតេស្ត"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ទូទៅ"</string> </resources> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index a69601ad937f..bb99b5d56290 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ಕೀಬೋರ್ಡ್ ಲೇಔಟ್ ಅನ್ನು <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> ಗೆ ಸೆಟ್ ಮಾಡಲಾಗಿದೆ… ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ಗಳನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ಕೀಬೋರ್ಡ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ಖಾಸಗಿ"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ಕ್ಲೋನ್"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ಕೆಲಸ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ಕೆಲಸ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ಕೆಲಸ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 77d45c789563..82a3b2675d25 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"키보드 레이아웃이 <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>로 설정됩니다. 변경하려면 탭하세요."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"실제 키보드에 구성됨"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"키보드를 보려면 탭하세요."</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"비공개"</string> + <string name="profile_label_clone" msgid="769106052210954285">"복사"</string> + <string name="profile_label_work" msgid="3495359133038584618">"직장"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"직장 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"직장 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"테스트"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"공동"</string> </resources> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index ee7b40713624..11950aa25a31 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Баскычтопко төмөнкү калып коюлду: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Өзгөртүү үчүн басыңыз."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физикалык баскычтоптор конфигурацияланды"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Баскычтопторду көрүү үчүн басыңыз"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Купуя"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Жумуш"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Жумуш 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Жумуш 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string> </resources> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index d9698e320d45..3e887a506d4e 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ຕັ້ງໂຄງຮ່າງແປ້ນພິມເປັນ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… ແຕະເພື່ອປ່ຽນແປງ."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ຕັ້ງຄ່າແປ້ນພິມແທ້ແລ້ວ"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ແຕະເພື່ອເບິ່ງແປ້ນພິມ"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ສ່ວນຕົວ"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ໂຄລນ"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ວຽກ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ວຽກ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ວຽກ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 2a2a0883ac74..c77f78503c5f 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Palieskite, kad pakeistumėte."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Sukonfigūruotos fizinės klaviatūros"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Palieskite, kad peržiūrėtumėte klaviatūras"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privatu"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Identiška kopija"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Darbas"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Darbas (2)"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string> </resources> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 8c7c206aeae4..f443d8ef2e38 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Ir iestatīti šādi tastatūras izkārtojumi: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Lai to mainītu, pieskarieties."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziskās tastatūras ir konfigurētas"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Lai skatītu tastatūras, pieskarieties"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privāts"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klons"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Darbam"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Darbam (2.)"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string> </resources> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 4d2f72e5cc40..0c1d552ecd73 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Распоредот на тастатурата е поставен на <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Допрете за да промените."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физичките тастатури се конфигурирани"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Допрете за да ги видите тастатурите"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Приватен профил"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клониран профил"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Работен профил"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Работен профил 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string> </resources> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index be405751eca3..18eb4a941d49 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"കീബോർഡ് ലേഔട്ട് ആയി ഇനിപ്പറയുന്നവ സജ്ജീകരിച്ചു: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… മാറ്റാൻ ടാപ്പ് ചെയ്യുക."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"യഥാർത്ഥ കീബോർഡുകൾ കോൺഫിഗർ ചെയ്തു"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"കീബോർഡുകൾ കാണാൻ ടാപ്പ് ചെയ്യുക"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"സ്വകാര്യം"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ക്ലോൺ ചെയ്യുക"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ഔദ്യോഗികം"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ഔദ്യോഗികം 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ഔദ്യോഗികം 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ടെസ്റ്റ്"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string> </resources> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index c215b88a6d8e..ed656cd24f7c 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Гарын бүдүүвчийг <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> болгож тохируулсан… Өөрчлөхийн тулд товшино уу."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Биет гарыг тохируулсан"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Гарыг харахын тулд товшино уу"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Хувийн"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Ажил"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Ажил 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Ажил 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string> </resources> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 03fcc1767a75..ff475fe2a971 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> वर सेट करा… बदलण्यासाठी टॅप करा."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"वास्तविक कीबोर्ड कॉंफिगर केला"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"कीबोर्ड पाहण्यासाठी टॅप करा"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"खाजगी"</string> + <string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ऑफिस"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ऑफिस २"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफिस ३"</string> + <string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string> </resources> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 84b79a806904..4821fc896275 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Reka letak papan kekunci ditetapkan kepada <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Ketik untuk menukar reka letak."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Papan kekunci fizikal dikonfigurasikan"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Ketik untuk melihat papan kekunci"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Peribadi"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Kerja"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Kerja 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string> </resources> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 01906b91f6e9..6981e82c2125 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ကီးဘုတ်အပြင်အဆင်ကို <xliff:g id="LAYOUT_1">%1$s</xliff:g>၊ <xliff:g id="LAYOUT_2">%2$s</xliff:g>၊ <xliff:g id="LAYOUT_3">%3$s</xliff:g> သို့ သတ်မှတ်လိုက်သည်… ပြောင်းရန် တို့ပါ။"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ပကတိကီးဘုတ်များကို စီစဉ်သတ်မှတ်ထားသည်"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ကီးဘုတ်များကြည့်ရန် တို့ပါ"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"သီးသန့်"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ဆင်တူပြုလုပ်ရန်"</string> + <string name="profile_label_work" msgid="3495359133038584618">"အလုပ်"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"အလုပ် ၂"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"အလုပ် ၃"</string> + <string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index d8e3b40ee46e..e667b93baf31 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Tastaturoppsettet er satt til <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> … Trykk for å endre det."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"De fysiske tastaturene er konfigurert"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Trykk for å se tastaturene"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Jobb"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Jobb 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string> </resources> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 126f61277491..bb06243b0fce 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"किबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> भाषामा सेट गरिएको छ… बदल्न ट्याप गर्नुहोस्।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"भौतिक किबोर्डहरू कन्फिगर गरिएका छन्"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"किबोर्डहरू हेर्न ट्याप गर्नुहोस्"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"निजी"</string> + <string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string> + <string name="profile_label_work" msgid="3495359133038584618">"कार्य प्रोफाइल"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"कार्य प्रोफाइल २"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"कार्य प्रोफाइल ३"</string> + <string name="profile_label_test" msgid="9168641926186071947">"परीक्षण"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string> </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index bc187978fb78..40239a6fef91 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Toetsenbordindeling ingesteld op <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Tik om te wijzigen."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fysieke toetsenborden zijn ingesteld"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tik om toetsenborden te bekijken"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privé"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Kloon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Werk"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Werk 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string> </resources> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 7f66887bee92..fc4824d44a70 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"କୀବୋର୍ଡ ଲେଆଉଟକୁ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>ରେ ସେଟ କରାଯାଇଛି… ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ଫିଜିକାଲ କୀବୋର୍ଡଗୁଡ଼ିକୁ କନଫିଗର କରାଯାଇଛି"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"କୀବୋର୍ଡଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ପ୍ରାଇଭେଟ"</string> + <string name="profile_label_clone" msgid="769106052210954285">"କ୍ଲୋନ"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ୱାର୍କ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ୱାର୍କ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ୱାର୍କ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string> </resources> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 039614931a64..f828daef690f 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ਕੀ-ਬੋਰਡ ਦਾ ਖਾਕਾ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> \'ਤੇ ਸੈੱਟ ਹੈ… ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡਾਂ ਦਾ ਸੰਰੂਪਣ ਕੀਤਾ ਗਿਆ"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ਕੀ-ਬੋਰਡਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ਨਿੱਜੀ"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ਕਲੋਨ"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ਕਾਰਜ"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ਕੰਮ ਸੰਬੰਧੀ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ਕੰਮ ਸੰਬੰਧੀ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 66d5570e0379..f083436dfe54 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Ustawiono układ klawiatury <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Kliknij, aby to zmienić."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Skonfigurowano klawiatury fizyczne"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Kliknij, aby wyświetlić klawiatury"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Prywatny"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Służbowy"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Służbowy 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Służbowy 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string> </resources> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index e32c7389137d..16344880c4b2 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Layout do teclado definido como <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toque para mudar."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toque para conferir os teclados"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Particular"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Trabalho"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Trabalho 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index e32c7389137d..16344880c4b2 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Layout do teclado definido como <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toque para mudar."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toque para conferir os teclados"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Particular"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Trabalho"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Trabalho 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 2f16db38d2df..cda23a5c4af6 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Настроены раскладки клавиатуры для яз.: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> и др. Нажмите, чтобы изменить."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физические клавиатуры настроены"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Нажмите, чтобы посмотреть подключенные клавиатуры."</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Личный"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Рабочий"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Рабочий 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Рабочий 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string> </resources> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index ac86d7b1979f..4e26aa2aa192 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"යතුරු පුවරුව <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> ලෙස සකසා ඇත… වෙනස් කිරීමට තට්ටු කරන්න."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"භෞතික යතුරු පුවරුව වින්යාස කෙරිණි"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"යතුරු පුවරු බැලීමට තට්ටු කරන්න"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"පෞද්ගලික"</string> + <string name="profile_label_clone" msgid="769106052210954285">"ක්ලෝන කරන්න"</string> + <string name="profile_label_work" msgid="3495359133038584618">"කාර්යාලය"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"කාර්යාලය 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"කාර්යාලය 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string> </resources> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index d1d7897f4242..089372ef12e6 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozloženie klávesnice je nastavené na jazyky <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> a <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Môžete to zmeniť klepnutím."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice sú nakonfigurované"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klávesnice si zobrazíte klepnutím"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Súkromný"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Pracovný"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"2. pracovný"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Spoločný"</string> </resources> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 2cd76fdbee62..89658018d36c 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Struktura e tastierës u caktua në: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Trokit për ta ndryshuar."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Tastierat fizike u konfiguruan"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Trokit për të parë tastierat"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Puna"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Puna 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string> </resources> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 9e77bf68cfe6..f81d24da167c 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2360,18 +2360,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Распоред тастатуре је подешен на <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Додирните да бисте променили."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физичке тастатуре су конфигурисане"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Додирните да бисте видели тастатуре"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Приватно"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Клонирано"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Посао"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Посао 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Посао 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Тест"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string> </resources> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index ab5bfca9f377..994ea616d604 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Tangentbordslayouten är inställd på <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> … Tryck om du vill ändra."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fysiska tangentbord har konfigurerats"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Tryck för att visa tangentbord"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Arbete"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Arbete 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string> </resources> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 61c0b1754a26..912c58874937 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Muundo wa kibodi umewekwa kuwa <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Gusa ili ubadilishe."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Mipangilio ya kibodi halisi imewekwa"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Gusa ili uangalie kibodi"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Wa faragha"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Nakala"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Wa kazini"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Wa 2 wa Kazini"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string> </resources> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index edf6c2da383e..8ba07fba6800 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"கீபோர்டு தளவமைப்பு <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… ஆகிய மொழிகளில் அமைக்கப்பட்டது, மாற்ற தட்டவும்."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"கீபோர்டுகள் உள்ளமைக்கப்பட்டன"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"கீபோர்டுகளைப் பார்க்க தட்டவும்"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"தனிப்பட்டது"</string> + <string name="profile_label_clone" msgid="769106052210954285">"குளோன்"</string> + <string name="profile_label_work" msgid="3495359133038584618">"பணி"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"பணி 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"பணி 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string> </resources> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index d862e2f57651..ce7b8bcb35ac 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"కీబోర్డ్ లేఅవుట్ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>కు సెట్ చేయబడింది… మార్చడానికి ట్యాప్ చేయండి."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ఫిజికల్ కీబోర్డ్లు కాన్ఫిగర్ చేయబడ్డాయి"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"కీబోర్డ్లను చూడటానికి ట్యాప్ చేయండి"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ప్రైవేట్"</string> + <string name="profile_label_clone" msgid="769106052210954285">"క్లోన్ చేయండి"</string> + <string name="profile_label_work" msgid="3495359133038584618">"ఆఫీస్"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"ఆఫీస్ 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"ఆఫీస్ 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"పరీక్ష"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"కమ్యూనల్"</string> </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index bda7e8ad40a4..b26389e664ce 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ตั้งค่ารูปแบบแป้นพิมพ์เป็นภาษา<xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… แตะเพื่อเปลี่ยน"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"กำหนดค่าแป้นพิมพ์จริงแล้ว"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"แตะเพื่อดูแป้นพิมพ์"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"ส่วนตัว"</string> + <string name="profile_label_clone" msgid="769106052210954285">"โคลน"</string> + <string name="profile_label_work" msgid="3495359133038584618">"งาน"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"งาน 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"งาน 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string> </resources> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 53201ea1e07c..ceb8828ba27a 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Naitakda ang layout ng keyboard sa <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… I-tap para baguhin."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Na-configure ang mga pisikal na keyboard"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"I-tap para tingnan ang mga keyboard"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Pribado"</string> + <string name="profile_label_clone" msgid="769106052210954285">"I-clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Trabaho"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Trabaho 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index ee1168f64114..528d1e542193 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klavye düzeni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> olarak ayarlandı… Değiştirmek için dokunun."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziksel klavyeler yapılandırıldı"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klavyeleri görüntülemek için dokunun"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Gizli"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> + <string name="profile_label_work" msgid="3495359133038584618">"İş"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string> </resources> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index b1adf52f5562..b0421ee54173 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2361,18 +2361,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Вибрано такі розкладки клавіатури: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Натисніть, щоб змінити."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Фізичні клавіатури налаштовано"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Натисніть, щоб переглянути клавіатури"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Приватний профіль"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Копія профілю"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Робочий профіль"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Робочий профіль 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Робочий профіль 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string> </resources> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 92fd2664b259..767ee1c9757a 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"کی بورڈ لے آؤٹ <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g> پر سیٹ ہے… تبدیل کرنے کے لیے تھپتھپائیں۔"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"فزیکل کی بورڈز کنفیگر کئے گئے"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"کی بورڈز دیکھنے کے لیے تھپتھپائیں"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"نجی"</string> + <string name="profile_label_clone" msgid="769106052210954285">"کلون"</string> + <string name="profile_label_work" msgid="3495359133038584618">"دفتری پروفائل"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"دوسری دفتری پروفائل"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"تیسری دفتری پروفائل"</string> + <string name="profile_label_test" msgid="9168641926186071947">"ٹیسٹ"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"کمیونل"</string> </resources> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 79d9f9dc044c..e48d641785fb 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatura terilmasi bunga sozlandi: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Oʻzgartirish uchun ustiga bosing."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Tashqi klaviaturalar sozlandi"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klaviaturalarni ochish uchun ustiga bosing"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Yopiq"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Nusxasini yaratish"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Ish"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Ish 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string> </resources> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 2ddf49f16046..a14fa753915a 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Đã thiết lập bố cục bàn phím thành <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Hãy nhấn để thay đổi."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Đã định cấu hình bàn phím vật lý"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Nhấn để xem bàn phím"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Riêng tư"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Sao chép"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Công việc"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Công việc 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index f3b6a395568b..db98b3440496 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"键盘布局已设为<xliff:g id="LAYOUT_1">%1$s</xliff:g>、<xliff:g id="LAYOUT_2">%2$s</xliff:g>、<xliff:g id="LAYOUT_3">%3$s</xliff:g>…点按即可更改。"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"已配置物理键盘"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"点按即可查看键盘"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"私密"</string> + <string name="profile_label_clone" msgid="769106052210954285">"克隆"</string> + <string name="profile_label_work" msgid="3495359133038584618">"工作"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"工作 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"测试"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> </resources> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index bf72080874ca..b3e7959793cf 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"鍵盤版面配置已設定為<xliff:g id="LAYOUT_1">%1$s</xliff:g>、<xliff:g id="LAYOUT_2">%2$s</xliff:g>、<xliff:g id="LAYOUT_3">%3$s</xliff:g>…輕按即可變更。"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"已設定實體鍵盤"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"輕按即可查看鍵盤"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"私人"</string> + <string name="profile_label_clone" msgid="769106052210954285">"複製"</string> + <string name="profile_label_work" msgid="3495359133038584618">"工作"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"工作 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"測試"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 1158cfdc33e2..247499d3d30f 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"鍵盤配置已設為<xliff:g id="LAYOUT_1">%1$s</xliff:g>、<xliff:g id="LAYOUT_2">%2$s</xliff:g>、<xliff:g id="LAYOUT_3">%3$s</xliff:g>…輕觸即可變更。"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"已設定實體鍵盤"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"輕觸即可查看鍵盤"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"私人"</string> + <string name="profile_label_clone" msgid="769106052210954285">"副本"</string> + <string name="profile_label_work" msgid="3495359133038584618">"工作"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"工作 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"測試"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"通用"</string> </resources> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index db7c253f21d8..976a35e2aa11 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2359,18 +2359,11 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Uhlaka lwekhibhodi lusethelwe ku-<xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Thepha ukuze ushintshe."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Amakhibhodi aphathekayo amisiwe"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Thepha ukuze ubuke amakhibhodi"</string> - <!-- no translation found for profile_label_private (6463418670715290696) --> - <skip /> - <!-- no translation found for profile_label_clone (769106052210954285) --> - <skip /> - <!-- no translation found for profile_label_work (3495359133038584618) --> - <skip /> - <!-- no translation found for profile_label_work_2 (4691533661598632135) --> - <skip /> - <!-- no translation found for profile_label_work_3 (4834572253956798917) --> - <skip /> - <!-- no translation found for profile_label_test (9168641926186071947) --> - <skip /> - <!-- no translation found for profile_label_communal (8743921499944800427) --> - <skip /> + <string name="profile_label_private" msgid="6463418670715290696">"Okuyimfihlo"</string> + <string name="profile_label_clone" msgid="769106052210954285">"Yenza i-Clone"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Umsebenzi"</string> + <string name="profile_label_work_2" msgid="4691533661598632135">"Umsebenzi 2"</string> + <string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string> + <string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string> + <string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 698c5bad3801..e1c1a42eed81 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3854,6 +3854,11 @@ <!-- Specifies if an IME can only be used while a device is in VR mode or on a dedicated device --> <attr name="isVrOnly" format="boolean"/> + <!-- Specifies if an IME can only be used on a display created by a virtual device. + @see android.companion.virtual.VirtualDeviceParams.Builder#setInputMethodComponent + @hide @SystemApi --> + <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") --> + <attr name="isVirtualDeviceOnly" format="boolean"/> <attr name="__removed2" format="boolean" /> <!-- Specifies whether the IME supports showing inline suggestions. --> <attr name="supportsInlineSuggestions" format="boolean" /> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index f8546b73a37e..596cfe5a695d 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2515,6 +2515,10 @@ <attr name="versionMajor" format="integer" /> <!-- The SHA-256 digest of the SDK library signing certificate. --> <attr name="certDigest" format="string" /> + <!-- Specify whether the SDK is optional. The default is false, false means app can be + installed even if the SDK library doesn't exist, and the SDK library can be uninstalled + when the app is still installed. --> + <attr name="optional" format="boolean" /> </declare-styleable> <!-- The <code>static-library</code> tag declares that this apk is providing itself diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1229453e5736..387a1083c6d6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1404,6 +1404,9 @@ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. --> <integer name="config_shutdownBatteryTemperature">680</integer> + <!-- Shutdown if battery is critically low and the device is not powered. --> + <bool name="config_shutdownIfNoPower">true</bool> + <!-- Display low battery warning when battery level dips to this value --> <integer name="config_lowBatteryWarningLevel">20</integer> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 4cd4f638e191..f10e7f8c337e 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -112,6 +112,11 @@ <staging-public-group type="attr" first-id="0x01bd0000"> <!-- @FlaggedApi("android.content.res.default_locale") --> <public name="defaultLocale"/> + <!-- @FlaggedApi("android.companion.virtual.flags.vdm_custom_ime") + @hide @SystemApi --> + <public name="isVirtualDeviceOnly"/> + <!-- @FlaggedApi("android.content.pm.sdk_lib_independence") --> + <public name="optional"/> </staging-public-group> <staging-public-group type="id" first-id="0x01bc0000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 73a7e4296a57..1f6ac80a133c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -6312,11 +6312,6 @@ ul.</string> <!-- Content of connected display unavailable due to thermals notification. [CHAR LIMIT=NONE] --> <string name="connected_display_thermally_unavailable_notification_content">Your device is too warm and can\'t mirror to the display until it cools down</string> - <!-- Title of cable don't support displays notifications. [CHAR LIMIT=NONE] --> - <string name="connected_display_cable_dont_support_displays_notification_title">Cable may not support displays</string> - <!-- Content of cable don't support displays notification. [CHAR LIMIT=NONE] --> - <string name="connected_display_cable_dont_support_displays_notification_content">Your USB-C cable may not connect to displays properly</string> - <!-- Name of concurrent display notifications. [CHAR LIMIT=NONE] --> <string name="concurrent_display_notification_name">Dual screen</string> <!-- Title of concurrent display active notification. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 93aacdff57df..a5b102836816 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2094,6 +2094,7 @@ <java-symbol type="integer" name="config_autoBrightnessShortTermModelTimeout" /> <java-symbol type="integer" name="config_progressTimeoutFallbackHome" /> <java-symbol type="integer" name="config_shutdownBatteryTemperature" /> + <java-symbol type="bool" name="config_shutdownIfNoPower" /> <java-symbol type="integer" name="config_undockedHdmiRotation" /> <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> <java-symbol type="integer" name="config_brightness_ramp_rate_fast" /> @@ -5106,8 +5107,6 @@ <java-symbol type="string" name="connected_display_unavailable_notification_title"/> <java-symbol type="string" name="connected_display_unavailable_notification_content"/> <java-symbol type="string" name="connected_display_thermally_unavailable_notification_content"/> - <java-symbol type="string" name="connected_display_cable_dont_support_displays_notification_title"/> - <java-symbol type="string" name="connected_display_cable_dont_support_displays_notification_content"/> <java-symbol type="string" name="concurrent_display_notification_name"/> <java-symbol type="string" name="concurrent_display_notification_active_title"/> <java-symbol type="string" name="concurrent_display_notification_active_content"/> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 37f592f5797b..6706c916a0e2 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -167,12 +167,27 @@ android_library { android_ravenwood_test { name: "FrameworksCoreTestsRavenwood", + libs: [ + "android.test.runner", + "android.test.base", + ], static_libs: [ + "core-test-rules", // for libcore.dalvik.system.CloseGuardSupport + "androidx.core_core", "androidx.annotation_annotation", "androidx.test.rules", + "androidx.test.ext.junit", + "mockito_ravenwood", + "platform-test-annotations", + "flag-junit", ], srcs: [ + "src/android/os/BuildTest.java", "src/android/os/FileUtilsTest.java", + "src/android/util/**/*.java", + "src/com/android/internal/os/LongArrayMultiStateCounterTest.java", + "src/com/android/internal/util/**/*.java", + "testdoubles/src/com/android/internal/util/**/*.java", ], auto_gen_config: true, } diff --git a/core/tests/coretests/res/xml/ime_meta_virtual_device_only.xml b/core/tests/coretests/res/xml/ime_meta_virtual_device_only.xml new file mode 100644 index 000000000000..1905365808bc --- /dev/null +++ b/core/tests/coretests/res/xml/ime_meta_virtual_device_only.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<input-method + xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity" + android:isVirtualDeviceOnly="true"> +</input-method> diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index 2295eb989108..3162e6da15c5 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -16,19 +16,37 @@ package android.os; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.flag.junit.SetFlagsRule; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import junit.framework.Assert; -import junit.framework.TestCase; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Provides test cases for android.os.Build and, in turn, many of the * system properties set by the build system. */ -public class BuildTest extends TestCase { - +@RunWith(AndroidJUnit4.class) +public class BuildTest { private static final String TAG = "BuildTest"; + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + /** * Asserts that a String is non-null and non-empty. If it is not, * an AssertionFailedError is thrown with the given message. @@ -50,7 +68,9 @@ public class BuildTest extends TestCase { /** * Asserts that all android.os.Build fields are non-empty and/or in a valid range. */ + @Test @SmallTest + @IgnoreUnderRavenwood(blockedBy = Build.class) public void testBuildFields() throws Exception { assertNotEmpty("ID", Build.ID); assertNotEmpty("DISPLAY", Build.DISPLAY); @@ -72,4 +92,16 @@ public class BuildTest extends TestCase { // (e.g., must be a C identifier, must be a valid filename, must not contain any spaces) // add tests for them. } + + @Test + public void testFlagEnabled() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM); + assertTrue(Flags.androidOsBuildVanillaIceCream()); + } + + @Test + public void testFlagDisabled() throws Exception { + mSetFlagsRule.disableFlags(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM); + assertFalse(Flags.androidOsBuildVanillaIceCream()); + } } diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 7c4136d62b8f..9300d1e5cb95 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -831,6 +831,7 @@ public class DeviceConfigTest { } @Test + @FlakyTest(bugId = 299483542) public void onPropertiesChangedListener_setPropertiesCallback() { DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java index b212cf6b0803..1e444adbc362 100644 --- a/core/tests/coretests/src/android/util/ArrayMapTest.java +++ b/core/tests/coretests/src/android/util/ArrayMapTest.java @@ -14,11 +14,17 @@ package android.util; -import androidx.test.filters.LargeTest; +import static org.junit.Assert.fail; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; -import junit.framework.TestCase; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ConcurrentModificationException; @@ -26,10 +32,14 @@ import java.util.ConcurrentModificationException; * Unit tests for ArrayMap that don't belong in CTS. */ @LargeTest -public class ArrayMapTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class ArrayMapTest { private static final String TAG = "ArrayMapTest"; ArrayMap<String, String> map = new ArrayMap<>(); + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + /** * Attempt to generate a ConcurrentModificationException in ArrayMap. * <p> @@ -41,6 +51,7 @@ public class ArrayMapTest extends TestCase { * @throws Exception */ @Test + @IgnoreUnderRavenwood(reason = "Long test runtime") public void testConcurrentModificationException() throws Exception { final int TEST_LEN_MS = 5000; System.out.println("Starting ArrayMap concurrency test"); diff --git a/core/tests/coretests/src/android/util/ArraySetTest.java b/core/tests/coretests/src/android/util/ArraySetTest.java index f1bebfb9bd93..51de6341179b 100644 --- a/core/tests/coretests/src/android/util/ArraySetTest.java +++ b/core/tests/coretests/src/android/util/ArraySetTest.java @@ -16,12 +16,14 @@ package android.util; -import androidx.test.filters.LargeTest; +import static org.junit.Assert.fail; -import junit.framework.TestCase; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ConcurrentModificationException; @@ -29,7 +31,8 @@ import java.util.ConcurrentModificationException; * Unit tests for ArraySet that don't belong in CTS. */ @LargeTest -public class ArraySetTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class ArraySetTest { private static final String TAG = "ArraySetTest"; ArraySet<String> mSet = new ArraySet<>(); diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java index 15c51af0b3e1..b64826611f81 100644 --- a/core/tests/coretests/src/android/util/Base64Test.java +++ b/core/tests/coretests/src/android/util/Base64Test.java @@ -16,9 +16,16 @@ package android.util; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -32,7 +39,8 @@ import java.util.Random; import java.util.stream.Collectors; @LargeTest -public class Base64Test extends TestCase { +@RunWith(AndroidJUnit4.class) +public class Base64Test { private static final String TAG = "Base64Test"; /** Decodes a string, returning a string. */ @@ -62,7 +70,7 @@ public class Base64Test extends TestCase { } /** Assert that actual equals the first len bytes of expected. */ - private void assertEquals(byte[] expected, int len, byte[] actual) { + private void assertPartialEquals(byte[] expected, int len, byte[] actual) { assertEquals(len, actual.length); for (int i = 0; i < len; ++i) { assertEquals(expected[i], actual[i]); @@ -70,21 +78,14 @@ public class Base64Test extends TestCase { } /** Assert that actual equals the first len bytes of expected. */ - private void assertEquals(byte[] expected, int len, byte[] actual, int alen) { + private void assertPartialEquals(byte[] expected, int len, byte[] actual, int alen) { assertEquals(len, alen); for (int i = 0; i < len; ++i) { assertEquals(expected[i], actual[i]); } } - /** Assert that actual equals the first len bytes of expected. */ - private void assertEquals(byte[] expected, byte[] actual) { - assertEquals(expected.length, actual.length); - for (int i = 0; i < expected.length; ++i) { - assertEquals(expected[i], actual[i]); - } - } - + @Test public void testDecodeExtraChars() throws Exception { // padding 0 assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk")); @@ -131,28 +132,30 @@ public class Base64Test extends TestCase { (byte) 0xcc, (byte) 0xbb, (byte) 0xaa, (byte) 0x99, (byte) 0x88, (byte) 0x77 }; + @Test public void testBinaryDecode() throws Exception { - assertEquals(BYTES, 0, Base64.decode("", 0)); - assertEquals(BYTES, 1, Base64.decode("/w==", 0)); - assertEquals(BYTES, 2, Base64.decode("/+4=", 0)); - assertEquals(BYTES, 3, Base64.decode("/+7d", 0)); - assertEquals(BYTES, 4, Base64.decode("/+7dzA==", 0)); - assertEquals(BYTES, 5, Base64.decode("/+7dzLs=", 0)); - assertEquals(BYTES, 6, Base64.decode("/+7dzLuq", 0)); - assertEquals(BYTES, 7, Base64.decode("/+7dzLuqmQ==", 0)); - assertEquals(BYTES, 8, Base64.decode("/+7dzLuqmYg=", 0)); + assertPartialEquals(BYTES, 0, Base64.decode("", 0)); + assertPartialEquals(BYTES, 1, Base64.decode("/w==", 0)); + assertPartialEquals(BYTES, 2, Base64.decode("/+4=", 0)); + assertPartialEquals(BYTES, 3, Base64.decode("/+7d", 0)); + assertPartialEquals(BYTES, 4, Base64.decode("/+7dzA==", 0)); + assertPartialEquals(BYTES, 5, Base64.decode("/+7dzLs=", 0)); + assertPartialEquals(BYTES, 6, Base64.decode("/+7dzLuq", 0)); + assertPartialEquals(BYTES, 7, Base64.decode("/+7dzLuqmQ==", 0)); + assertPartialEquals(BYTES, 8, Base64.decode("/+7dzLuqmYg=", 0)); } + @Test public void testWebSafe() throws Exception { - assertEquals(BYTES, 0, Base64.decode("", Base64.URL_SAFE)); - assertEquals(BYTES, 1, Base64.decode("_w==", Base64.URL_SAFE)); - assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.URL_SAFE)); - assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.URL_SAFE)); - assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.URL_SAFE)); - assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.URL_SAFE)); - assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.URL_SAFE)); - assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.URL_SAFE)); - assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 0, Base64.decode("", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 1, Base64.decode("_w==", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 2, Base64.decode("_-4=", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 3, Base64.decode("_-7d", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.URL_SAFE)); + assertPartialEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.URL_SAFE)); assertEquals("", Base64.encodeToString(BYTES, 0, 0, Base64.URL_SAFE)); assertEquals("_w==\n", Base64.encodeToString(BYTES, 0, 1, Base64.URL_SAFE)); @@ -165,6 +168,7 @@ public class Base64Test extends TestCase { assertEquals("_-7dzLuqmYg=\n", Base64.encodeToString(BYTES, 0, 8, Base64.URL_SAFE)); } + @Test public void testFlags() throws Exception { assertEquals("YQ==\n", encodeToString("a", 0)); assertEquals("YQ==", encodeToString("a", Base64.NO_WRAP)); @@ -195,6 +199,7 @@ public class Base64Test extends TestCase { assertEquals("YWJjZA\r\n", encodeToString("abcd", Base64.CRLF | Base64.NO_PADDING)); } + @Test public void testLineLength() throws Exception { String in_56 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd"; String in_57 = in_56 + "e"; @@ -245,7 +250,9 @@ public class Base64Test extends TestCase { * us to get at package-private members (Base64.Encoder in * this case). */ - public void XXXtestEncodeInternal() throws Exception { + @Test + @Ignore + public void testEncodeInternal() throws Exception { byte[] input = { (byte) 0x61, (byte) 0x62, (byte) 0x63 }; byte[] output = new byte[100]; @@ -253,11 +260,11 @@ public class Base64Test extends TestCase { output); encoder.process(input, 0, 3, false); - assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op); + assertPartialEquals("YWJj".getBytes(), 4, encoder.output, encoder.op); assertEquals(0, encoder.tailLen); encoder.process(input, 0, 3, false); - assertEquals("YWJj".getBytes(), 4, encoder.output, encoder.op); + assertPartialEquals("YWJj".getBytes(), 4, encoder.output, encoder.op); assertEquals(0, encoder.tailLen); encoder.process(input, 0, 1, false); @@ -269,7 +276,7 @@ public class Base64Test extends TestCase { assertEquals(2, encoder.tailLen); encoder.process(input, 0, 1, false); - assertEquals("YWFh".getBytes(), 4, encoder.output, encoder.op); + assertPartialEquals("YWFh".getBytes(), 4, encoder.output, encoder.op); assertEquals(0, encoder.tailLen); encoder.process(input, 0, 2, false); @@ -277,15 +284,15 @@ public class Base64Test extends TestCase { assertEquals(2, encoder.tailLen); encoder.process(input, 0, 2, false); - assertEquals("YWJh".getBytes(), 4, encoder.output, encoder.op); + assertPartialEquals("YWJh".getBytes(), 4, encoder.output, encoder.op); assertEquals(1, encoder.tailLen); encoder.process(input, 0, 2, false); - assertEquals("YmFi".getBytes(), 4, encoder.output, encoder.op); + assertPartialEquals("YmFi".getBytes(), 4, encoder.output, encoder.op); assertEquals(0, encoder.tailLen); encoder.process(input, 0, 1, true); - assertEquals("YQ".getBytes(), 2, encoder.output, encoder.op); + assertPartialEquals("YQ".getBytes(), 2, encoder.output, encoder.op); } private static final String lipsum = @@ -311,6 +318,7 @@ public class Base64Test extends TestCase { "molestie dapibus commodo. Ut vel tellus at massa gravida " + "semper non sed orci."; + @Test public void testInputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, @@ -343,7 +351,7 @@ public class Base64Test extends TestCase { while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } - assertEquals(actual, ap, plain); + assertPartialEquals(actual, ap, plain); // read individual bytes bais = new ByteArrayInputStream(encoded); @@ -352,7 +360,7 @@ public class Base64Test extends TestCase { while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } - assertEquals(actual, ap, plain); + assertPartialEquals(actual, ap, plain); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(encoded); @@ -371,7 +379,7 @@ public class Base64Test extends TestCase { } } } - assertEquals(actual, ap, plain); + assertPartialEquals(actual, ap, plain); // ----- test encoding ("plain" -> "encoded") ----- @@ -382,7 +390,7 @@ public class Base64Test extends TestCase { while ((b = b64is.read(actual, ap, actual.length-ap)) != -1) { ap += b; } - assertEquals(actual, ap, encoded); + assertPartialEquals(actual, ap, encoded); // read individual bytes bais = new ByteArrayInputStream(plain); @@ -391,7 +399,7 @@ public class Base64Test extends TestCase { while ((b = b64is.read()) != -1) { actual[ap++] = (byte) b; } - assertEquals(actual, ap, encoded); + assertPartialEquals(actual, ap, encoded); // mix reads of variously-sized arrays with one-byte reads bais = new ByteArrayInputStream(plain); @@ -410,11 +418,12 @@ public class Base64Test extends TestCase { } } } - assertEquals(actual, ap, encoded); + assertPartialEquals(actual, ap, encoded); } } /** http://b/3026478 */ + @Test public void testSingleByteReads() throws IOException { InputStream in = new Base64InputStream( new ByteArrayInputStream("/v8=".getBytes()), Base64.DEFAULT); @@ -426,6 +435,7 @@ public class Base64Test extends TestCase { * Tests that Base64OutputStream produces exactly the same results * as calling Base64.encode/.decode on an in-memory array. */ + @Test public void testOutputStream() throws Exception { int[] flagses = { Base64.DEFAULT, Base64.NO_PADDING, @@ -456,7 +466,7 @@ public class Base64Test extends TestCase { b64os.write(plain); b64os.close(); actual = baos.toByteArray(); - assertEquals(encoded, actual); + assertArrayEquals(encoded, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); @@ -466,7 +476,7 @@ public class Base64Test extends TestCase { } b64os.close(); actual = baos.toByteArray(); - assertEquals(encoded, actual); + assertArrayEquals(encoded, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. @@ -489,7 +499,7 @@ public class Base64Test extends TestCase { } b64os.close(); actual = baos.toByteArray(); - assertEquals(encoded, actual); + assertArrayEquals(encoded, actual); // ----- test decoding ("encoded" -> "plain") ----- @@ -499,7 +509,7 @@ public class Base64Test extends TestCase { b64os.write(encoded); b64os.close(); actual = baos.toByteArray(); - assertEquals(plain, actual); + assertArrayEquals(plain, actual); // many calls to write(int) baos = new ByteArrayOutputStream(); @@ -509,7 +519,7 @@ public class Base64Test extends TestCase { } b64os.close(); actual = baos.toByteArray(); - assertEquals(plain, actual); + assertArrayEquals(plain, actual); // intermixed sequences of write(int) with // write(byte[],int,int) of various lengths. @@ -532,10 +542,11 @@ public class Base64Test extends TestCase { } b64os.close(); actual = baos.toByteArray(); - assertEquals(plain, actual); + assertArrayEquals(plain, actual); } } + @Test public void testOutputStream_ioExceptionDuringClose() { OutputStream out = new OutputStream() { @Override public void write(int b) throws IOException { } @@ -551,6 +562,7 @@ public class Base64Test extends TestCase { } } + @Test public void testOutputStream_ioExceptionDuringCloseAndWrite() { OutputStream out = new OutputStream() { @Override public void write(int b) throws IOException { @@ -583,6 +595,7 @@ public class Base64Test extends TestCase { } } + @Test public void testOutputStream_ioExceptionDuringWrite() { OutputStream out = new OutputStream() { @Override public void write(int b) throws IOException { diff --git a/core/tests/coretests/src/android/util/CharsetUtilsTest.java b/core/tests/coretests/src/android/util/CharsetUtilsTest.java index c29545168758..fbbe311d44fb 100644 --- a/core/tests/coretests/src/android/util/CharsetUtilsTest.java +++ b/core/tests/coretests/src/android/util/CharsetUtilsTest.java @@ -18,6 +18,9 @@ package android.util; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.HexDump; @@ -25,18 +28,25 @@ import com.android.internal.util.HexDump; import dalvik.system.VMRuntime; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = CharsetUtils.class) public class CharsetUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private byte[] dest; private long destPtr; @Before public void setUp() { - dest = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, 8); - destPtr = VMRuntime.getRuntime().addressOf(dest); + if (!RavenwoodRule.isUnderRavenwood()) { + dest = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, 8); + destPtr = VMRuntime.getRuntime().addressOf(dest); + } } @Test diff --git a/core/tests/coretests/src/android/util/CloseGuardTest.java b/core/tests/coretests/src/android/util/CloseGuardTest.java index d86c7b79fad6..15c57b1aa6f7 100644 --- a/core/tests/coretests/src/android/util/CloseGuardTest.java +++ b/core/tests/coretests/src/android/util/CloseGuardTest.java @@ -16,6 +16,9 @@ package android.util; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import libcore.dalvik.system.CloseGuardSupport; import org.junit.Rule; @@ -23,10 +26,21 @@ import org.junit.Test; import org.junit.rules.TestRule; /** Unit tests for {@link android.util.CloseGuard} */ +@IgnoreUnderRavenwood(blockedBy = CloseGuard.class) public class CloseGuardTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Rule - public final TestRule rule = CloseGuardSupport.getRule(); + public final TestRule rule; + + public CloseGuardTest() { + if (!RavenwoodRule.isUnderRavenwood()) { + rule = CloseGuardSupport.getRule(); + } else { + rule = null; + } + } @Test public void testEnabled_NotOpen() throws Throwable { diff --git a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java index 572e9b062097..72bd578ad8b9 100644 --- a/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java +++ b/core/tests/coretests/src/android/util/DayOfMonthCursorTest.java @@ -16,30 +16,40 @@ package android.util; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Calendar; /** * Unit tests for {@link DayOfMonthCursor}. */ -public class DayOfMonthCursorTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class DayOfMonthCursorTest { + @Test @SmallTest public void testMonthRows() { DayOfMonthCursor mc = new DayOfMonthCursor(2007, Calendar.SEPTEMBER, 11, Calendar.SUNDAY); - assertArraysEqual(new int[]{26, 27, 28, 29, 30, 31, 1}, + assertArrayEquals(new int[]{26, 27, 28, 29, 30, 31, 1}, mc.getDigitsForRow(0)); - assertArraysEqual(new int[]{2, 3, 4, 5, 6, 7, 8}, + assertArrayEquals(new int[]{2, 3, 4, 5, 6, 7, 8}, mc.getDigitsForRow(1)); - assertArraysEqual(new int[]{30, 1, 2, 3, 4, 5, 6}, + assertArrayEquals(new int[]{30, 1, 2, 3, 4, 5, 6}, mc.getDigitsForRow(5)); } + @Test @SmallTest public void testMoveLeft() { DayOfMonthCursor mc = new DayOfMonthCursor(2007, @@ -70,6 +80,7 @@ public class DayOfMonthCursorTest extends TestCase { assertEquals(5, mc.getSelectedColumn()); } + @Test @SmallTest public void testMoveRight() { DayOfMonthCursor mc = new DayOfMonthCursor(2007, @@ -100,6 +111,7 @@ public class DayOfMonthCursorTest extends TestCase { assertEquals(1, mc.getSelectedColumn()); } + @Test @SmallTest public void testMoveUp() { DayOfMonthCursor mc = new DayOfMonthCursor(2007, @@ -124,6 +136,7 @@ public class DayOfMonthCursorTest extends TestCase { assertEquals(4, mc.getSelectedColumn()); } + @Test @SmallTest public void testMoveDown() { DayOfMonthCursor mc = new DayOfMonthCursor(2007, @@ -147,12 +160,4 @@ public class DayOfMonthCursorTest extends TestCase { assertEquals(1, mc.getSelectedRow()); assertEquals(0, mc.getSelectedColumn()); } - - private void assertArraysEqual(int[] expected, int[] actual) { - assertEquals("array length", expected.length, actual.length); - for (int i = 0; i < expected.length; i++) { - assertEquals("index " + i, - expected[i], actual[i]); - } - } } diff --git a/core/tests/coretests/src/android/util/HashedStringCacheTest.java b/core/tests/coretests/src/android/util/HashedStringCacheTest.java index 229247353a52..08c85ac5c0d6 100644 --- a/core/tests/coretests/src/android/util/HashedStringCacheTest.java +++ b/core/tests/coretests/src/android/util/HashedStringCacheTest.java @@ -26,28 +26,37 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Environment; import android.os.storage.StorageManager; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.InstrumentationRegistry; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.io.File; - /** * Unit tests for {@link HashedStringCache}. */ +@IgnoreUnderRavenwood(blockedBy = HashedStringCache.class) public class HashedStringCacheTest { private static final String TAG = "HashedStringCacheTest"; private Context mContext; private static final String TEST_STRING = "test_string"; + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Before public void setup() { - mContext = null; - mContext = InstrumentationRegistry.getContext(); - clearSharedPreferences(); + if (!RavenwoodRule.isUnderRavenwood()) { + mContext = InstrumentationRegistry.getContext(); + clearSharedPreferences(); + } else { + mContext = null; + } } @Test diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java index 370885d3d4ac..475e347141df 100644 --- a/core/tests/coretests/src/android/util/LogNullabilityTest.java +++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java @@ -19,6 +19,8 @@ package android.util; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -35,8 +37,10 @@ public final class LogNullabilityTest { Log.i(null, ""); Log.w(null, ""); Log.e(null, ""); - Log.wtf(null, ""); - Log.wtfStack(null, ""); + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf(null, ""); + Log.wtfStack(null, ""); + } Log.println(Log.INFO, null, ""); // Implicit assertions of not crashing. @@ -49,7 +53,9 @@ public final class LogNullabilityTest { Log.i(null, "", new Throwable()); Log.w(null, "", new Throwable()); Log.e(null, "", new Throwable()); - Log.wtf(null, "", new Throwable()); + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf(null, "", new Throwable()); + } Log.printlns(Log.LOG_ID_MAIN, Log.INFO, null, "", new Throwable()); // Implicit assertions of not crashing. @@ -84,8 +90,10 @@ public final class LogNullabilityTest { } catch (NullPointerException expected) { } - Log.wtf("", (String) null); - Log.wtfStack("", (String) null); + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf("", (String) null); + Log.wtfStack("", (String) null); + } // Implicit assertion of not crashing. @@ -103,15 +111,10 @@ public final class LogNullabilityTest { Log.i("", null, new Throwable()); Log.w("", null, new Throwable()); Log.e("", null, new Throwable()); - Log.wtf("", null, new Throwable()); - - // Implicit assertions of not crashing. - - try { - Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, new Throwable()); - fail(); - } catch (NullPointerException expected) { + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf("", null, new Throwable()); } + Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, new Throwable()); } @Test @@ -121,7 +124,9 @@ public final class LogNullabilityTest { Log.i("", "", null); Log.w("", "", null); Log.e("", "", null); - Log.wtf("", "", null); + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf("", "", null); + } // Warning has its own (String, Throwable) overload. Log.w("", (Throwable) null); @@ -131,10 +136,12 @@ public final class LogNullabilityTest { // Implicit assertions of not crashing. // WTF has its own (String, Throwable) overload with different behavior. - try { - Log.wtf("", (Throwable) null); - fail(); - } catch (NullPointerException expected) { + if (!RavenwoodRule.isUnderRavenwood()) { + try { + Log.wtf("", (Throwable) null); + fail(); + } catch (NullPointerException expected) { + } } } @@ -145,15 +152,10 @@ public final class LogNullabilityTest { Log.i("", null, null); Log.w("", null, null); Log.e("", null, null); - Log.wtf("", null, null); - - // Implicit assertions of not crashing. - - try { - Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, null); - fail(); - } catch (NullPointerException expected) { + if (!RavenwoodRule.isUnderRavenwood()) { + Log.wtf("", null, null); } + Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, null); } @Test diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java index d783c127d8c4..f9966a1cf65d 100644 --- a/core/tests/coretests/src/android/util/LogTest.java +++ b/core/tests/coretests/src/android/util/LogTest.java @@ -20,23 +20,24 @@ import android.os.SystemProperties; import android.test.PerformanceTestCase; import androidx.test.filters.Suppress; +import androidx.test.runner.AndroidJUnit4; import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + //This is an empty TestCase. @Suppress -public class LogTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class LogTest { private static final String PROPERTY_TAG = "log.tag.LogTest"; private static final String LOG_TAG = "LogTest"; - - // TODO: remove this test once we uncomment out the following test. - public void testLogTestDummy() { - return; - } - - - /* TODO: This test is commented out because we will not be able to set properities. Fix the test. + @Test + @Ignore public void testIsLoggable() { // First clear any SystemProperty setting for our test key. SystemProperties.set(PROPERTY_TAG, null); @@ -129,8 +130,7 @@ public class LogTest extends TestCase { Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ERROR)); Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ASSERT)); } - */ - + public static class PerformanceTest extends TestCase implements PerformanceTestCase { private static final int ITERATIONS = 1000; diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java index a1d48e689427..9dbaae0d0555 100644 --- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java +++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java @@ -16,9 +16,13 @@ package android.util; +import static org.junit.Assert.assertEquals; + import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.HashMap; import java.util.Iterator; @@ -29,9 +33,11 @@ import java.util.Random; * Tests for {@link LongSparseLongArray}. */ @LargeTest -public class LongSparseLongArrayTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class LongSparseLongArrayTest { private static final String TAG = "LongSparseLongArrayTest"; + @Test public void testSimplePut() throws Exception { final LongSparseLongArray array = new LongSparseLongArray(5); for (int i = 0; i < 48; i++) { @@ -45,6 +51,7 @@ public class LongSparseLongArrayTest extends TestCase { } } + @Test public void testSimplePutBackwards() throws Exception { final LongSparseLongArray array = new LongSparseLongArray(5); for (int i = 47; i >= 0; i--) { @@ -58,6 +65,7 @@ public class LongSparseLongArrayTest extends TestCase { } } + @Test public void testMiddleInsert() throws Exception { final LongSparseLongArray array = new LongSparseLongArray(5); for (int i = 0; i < 48; i++) { @@ -74,6 +82,7 @@ public class LongSparseLongArrayTest extends TestCase { assertEquals(1024, array.get(special, -1)); } + @Test public void testFuzz() throws Exception { final Random r = new Random(); diff --git a/core/tests/coretests/src/android/util/LruCacheTest.java b/core/tests/coretests/src/android/util/LruCacheTest.java index 1928bfdfc61d..10e8308e7964 100644 --- a/core/tests/coretests/src/android/util/LruCacheTest.java +++ b/core/tests/coretests/src/android/util/LruCacheTest.java @@ -16,7 +16,14 @@ package android.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; @@ -24,13 +31,15 @@ import java.util.Collections; import java.util.List; import java.util.Map; -public final class LruCacheTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public final class LruCacheTest { private int expectedCreateCount; private int expectedPutCount; private int expectedHitCount; private int expectedMissCount; private int expectedEvictionCount; + @Test public void testStatistics() { LruCache<String, String> cache = new LruCache<String, String>(3); assertStatistics(cache); @@ -80,6 +89,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "e", "E", "b", "B", "c", "C"); } + @Test public void testStatisticsWithCreate() { LruCache<String, String> cache = newCreatingCache(); assertStatistics(cache); @@ -104,18 +114,21 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "cc", "created-cc", "dd", "created-dd", "aa", "created-aa"); } + @Test public void testCreateOnCacheMiss() { LruCache<String, String> cache = newCreatingCache(); String created = cache.get("aa"); assertEquals("created-aa", created); } + @Test public void testNoCreateOnCacheHit() { LruCache<String, String> cache = newCreatingCache(); cache.put("aa", "put-aa"); assertEquals("put-aa", cache.get("aa")); } + @Test public void testConstructorDoesNotAllowZeroCacheSize() { try { new LruCache<String, String>(0); @@ -124,6 +137,7 @@ public final class LruCacheTest extends TestCase { } } + @Test public void testCannotPutNullKey() { LruCache<String, String> cache = new LruCache<String, String>(3); try { @@ -133,6 +147,7 @@ public final class LruCacheTest extends TestCase { } } + @Test public void testCannotPutNullValue() { LruCache<String, String> cache = new LruCache<String, String>(3); try { @@ -142,6 +157,7 @@ public final class LruCacheTest extends TestCase { } } + @Test public void testToString() { LruCache<String, String> cache = new LruCache<String, String>(3); assertEquals("LruCache[maxSize=3,hits=0,misses=0,hitRate=0%]", cache.toString()); @@ -160,6 +176,7 @@ public final class LruCacheTest extends TestCase { assertEquals("LruCache[maxSize=3,hits=3,misses=2,hitRate=60%]", cache.toString()); } + @Test public void testEvictionWithSingletonCache() { LruCache<String, String> cache = new LruCache<String, String>(1); cache.put("a", "A"); @@ -167,6 +184,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "b", "B"); } + @Test public void testEntryEvictedWhenFull() { List<String> log = new ArrayList<String>(); LruCache<String, String> cache = newRemovalLogCache(log); @@ -184,6 +202,7 @@ public final class LruCacheTest extends TestCase { * Replacing the value for a key doesn't cause an eviction but it does bring * the replaced entry to the front of the queue. */ + @Test public void testPutCauseEviction() { List<String> log = new ArrayList<String>(); LruCache<String, String> cache = newRemovalLogCache(log); @@ -196,6 +215,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "a", "A", "c", "C", "b", "B2"); } + @Test public void testCustomSizesImpactsSize() { LruCache<String, String> cache = new LruCache<String, String>(10) { @Override protected int sizeOf(String key, String value) { @@ -212,6 +232,7 @@ public final class LruCacheTest extends TestCase { assertEquals(6, cache.size()); } + @Test public void testEvictionWithCustomSizes() { LruCache<String, String> cache = new LruCache<String, String>(4) { @Override protected int sizeOf(String key, String value) { @@ -241,6 +262,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "j", "JJJ"); } + @Test public void testEvictionThrowsWhenSizesAreInconsistent() { LruCache<String, int[]> cache = new LruCache<String, int[]>(4) { @Override protected int sizeOf(String key, int[] value) { @@ -263,6 +285,7 @@ public final class LruCacheTest extends TestCase { } } + @Test public void testEvictionThrowsWhenSizesAreNegative() { LruCache<String, String> cache = new LruCache<String, String>(4) { @Override protected int sizeOf(String key, String value) { @@ -282,6 +305,7 @@ public final class LruCacheTest extends TestCase { * because evicting a small element may be insufficient to make room for a * large element. */ + @Test public void testDifferentElementSizes() { LruCache<String, String> cache = new LruCache<String, String>(10) { @Override protected int sizeOf(String key, String value) { @@ -299,6 +323,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache, "e", "12345678"); } + @Test public void testEvictAll() { List<String> log = new ArrayList<String>(); LruCache<String, String> cache = newRemovalLogCache(log); @@ -310,6 +335,7 @@ public final class LruCacheTest extends TestCase { assertEquals(Arrays.asList("a=A", "b=B", "c=C"), log); } + @Test public void testEvictAllEvictsSizeZeroElements() { LruCache<String, String> cache = new LruCache<String, String>(10) { @Override protected int sizeOf(String key, String value) { @@ -323,6 +349,7 @@ public final class LruCacheTest extends TestCase { assertSnapshot(cache); } + @Test public void testRemoveWithCustomSizes() { LruCache<String, String> cache = new LruCache<String, String>(10) { @Override protected int sizeOf(String key, String value) { @@ -335,6 +362,7 @@ public final class LruCacheTest extends TestCase { assertEquals(4, cache.size()); } + @Test public void testRemoveAbsentElement() { LruCache<String, String> cache = new LruCache<String, String>(10); cache.put("a", "A"); @@ -343,6 +371,7 @@ public final class LruCacheTest extends TestCase { assertEquals(2, cache.size()); } + @Test public void testRemoveNullThrows() { LruCache<String, String> cache = new LruCache<String, String>(10); try { @@ -352,6 +381,7 @@ public final class LruCacheTest extends TestCase { } } + @Test public void testRemoveCallsEntryRemoved() { List<String> log = new ArrayList<String>(); LruCache<String, String> cache = newRemovalLogCache(log); @@ -360,6 +390,7 @@ public final class LruCacheTest extends TestCase { assertEquals(Arrays.asList("a=A>null"), log); } + @Test public void testPutCallsEntryRemoved() { List<String> log = new ArrayList<String>(); LruCache<String, String> cache = newRemovalLogCache(log); @@ -368,6 +399,7 @@ public final class LruCacheTest extends TestCase { assertEquals(Arrays.asList("a=A>A2"), log); } + @Test public void testEntryRemovedIsCalledWithoutSynchronization() { LruCache<String, String> cache = new LruCache<String, String>(3) { @Override protected void entryRemoved( @@ -385,6 +417,7 @@ public final class LruCacheTest extends TestCase { cache.evictAll(); // multiple eviction } + @Test public void testCreateIsCalledWithoutSynchronization() { LruCache<String, String> cache = new LruCache<String, String>(3) { @Override protected String create(String key) { @@ -401,6 +434,7 @@ public final class LruCacheTest extends TestCase { * working. The map value should be returned by get(), and the created value * should be released with entryRemoved(). */ + @Test public void testCreateWithConcurrentPut() { final List<String> log = new ArrayList<String>(); LruCache<String, String> cache = new LruCache<String, String>(3) { @@ -423,6 +457,7 @@ public final class LruCacheTest extends TestCase { * the first create to return is returned by both gets. The other created * values should be released with entryRemove(). */ + @Test public void testCreateWithConcurrentCreate() { final List<String> log = new ArrayList<String>(); LruCache<String, Integer> cache = new LruCache<String, Integer>(3) { diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java index 30d5f778e945..06f970fcf77c 100644 --- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java +++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java @@ -16,18 +16,26 @@ package android.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Calendar; /** * Unit tests for {@link MonthDisplayHelper}. */ -public class MonthDisplayHelperTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class MonthDisplayHelperTest { + @Test @SmallTest public void testFirstDayOfMonth() { @@ -40,12 +48,14 @@ public class MonthDisplayHelperTest extends TestCase { new MonthDisplayHelper(2007, Calendar.SEPTEMBER).getFirstDayOfMonth()); } + @Test @SmallTest public void testNumberOfDaysInCurrentMonth() { assertEquals(30, new MonthDisplayHelper(2007, Calendar.SEPTEMBER).getNumberOfDaysInMonth()); } + @Test @SmallTest public void testMonthRows() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, Calendar.SEPTEMBER); @@ -59,6 +69,7 @@ public class MonthDisplayHelperTest extends TestCase { } + @Test @SmallTest public void testMonthRowsWeekStartsMonday() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -74,6 +85,7 @@ public class MonthDisplayHelperTest extends TestCase { helper.getDigitsForRow(5)); } + @Test @SmallTest public void testMonthRowsWeekStartsSaturday() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -98,6 +110,7 @@ public class MonthDisplayHelperTest extends TestCase { helper.getDigitsForRow(4)); } + @Test @SmallTest public void testGetDayAt() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -109,6 +122,7 @@ public class MonthDisplayHelperTest extends TestCase { assertEquals(2, helper.getDayAt(5, 2)); } + @Test @SmallTest public void testPrevMonth() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -124,6 +138,7 @@ public class MonthDisplayHelperTest extends TestCase { helper.getDigitsForRow(0)); } + @Test @SmallTest public void testPrevMonthRollOver() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -135,6 +150,7 @@ public class MonthDisplayHelperTest extends TestCase { assertEquals(Calendar.DECEMBER, helper.getMonth()); } + @Test @SmallTest public void testNextMonth() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -150,6 +166,7 @@ public class MonthDisplayHelperTest extends TestCase { helper.getDigitsForRow(0)); } + @Test @SmallTest public void testGetRowOf() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -162,6 +179,7 @@ public class MonthDisplayHelperTest extends TestCase { assertEquals(3, helper.getRowOf(19)); } + @Test @SmallTest public void testGetColumnOf() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, @@ -174,6 +192,7 @@ public class MonthDisplayHelperTest extends TestCase { assertEquals(0, helper.getColumnOf(26)); } + @Test @SmallTest public void testWithinCurrentMonth() { MonthDisplayHelper helper = new MonthDisplayHelper(2007, diff --git a/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java b/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java index 4de27c332d96..ce1eabc741a3 100644 --- a/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java +++ b/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java @@ -31,10 +31,15 @@ import static java.lang.String.join; import static java.util.Arrays.asList; import android.net.Network; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -51,7 +56,10 @@ import java.util.stream.Stream; @SmallTest @RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = NtpTrustedTime.class) public class NtpTrustedTimeTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private static final Duration VALID_TIMEOUT = Duration.ofSeconds(5); diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java index dd8f73fd54c4..a180ec3f9e35 100644 --- a/core/tests/coretests/src/android/util/PatternsTest.java +++ b/core/tests/coretests/src/android/util/PatternsTest.java @@ -16,17 +16,25 @@ package android.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class PatternsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class PatternsTest { // Tests for Patterns.TOP_LEVEL_DOMAIN + @Test @SmallTest public void testTldPattern() throws Exception { boolean t; @@ -58,36 +66,42 @@ public class PatternsTest extends TestCase { // Tests for Patterns.IANA_TOP_LEVEL_DOMAINS + @Test @SmallTest public void testIanaTopLevelDomains_matchesValidTld() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); assertTrue("Should match 'com'", pattern.matcher("com").matches()); } + @Test @SmallTest public void testIanaTopLevelDomains_matchesValidNewTld() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); assertTrue("Should match 'me'", pattern.matcher("me").matches()); } + @Test @SmallTest public void testIanaTopLevelDomains_matchesPunycodeTld() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); assertTrue("Should match Punycode TLD", pattern.matcher("xn--qxam").matches()); } + @Test @SmallTest public void testIanaTopLevelDomains_matchesIriTLD() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); assertTrue("Should match IRI TLD", pattern.matcher("\uD55C\uAD6D").matches()); } + @Test @SmallTest public void testIanaTopLevelDomains_doesNotMatchWrongTld() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); assertFalse("Should not match 'mem'", pattern.matcher("mem").matches()); } + @Test @SmallTest public void testIanaTopLevelDomains_doesNotMatchWrongPunycodeTld() throws Exception { Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS); @@ -96,6 +110,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.WEB_URL + @Test @SmallTest public void testWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception { String url = "http://www.android.com"; @@ -103,6 +118,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesValidUrlWithSchemeHostnameAndNewTld() throws Exception { String url = "http://www.android.me"; @@ -110,6 +126,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesValidUrlWithHostnameAndNewTld() throws Exception { String url = "android.me"; @@ -117,6 +134,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesChinesePunycodeUrlWithProtocol() throws Exception { String url = "http://xn--fsqu00a.xn--0zwm56d"; @@ -124,6 +142,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesChinesePunycodeUrlWithoutProtocol() throws Exception { String url = "xn--fsqu00a.xn--0zwm56d"; @@ -131,6 +150,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesArabicPunycodeUrlWithProtocol() throws Exception { String url = "http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx"; @@ -138,6 +158,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesArabicPunycodeUrlWithoutProtocol() throws Exception { String url = "xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx"; @@ -145,6 +166,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithUnicodeDomainNameWithProtocol() throws Exception { String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -152,6 +174,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithUnicodeDomainNameWithoutProtocol() throws Exception { String url = "\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -159,6 +182,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithUnicodeTld() throws Exception { String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D"; @@ -166,6 +190,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithUnicodePath() throws Exception { String url = "http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" + @@ -174,19 +199,22 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_doesNotMatchValidUrlWithInvalidProtocol() throws Exception { - String url = "ftp://www.example.com"; + String url = "invalid://www.example.com"; assertFalse("Should not match URL with invalid protocol", Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesValidUrlWithPort() throws Exception { String url = "http://www.example.com:8080"; assertTrue("Should match URL with port", Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithPortAndQuery() throws Exception { String url = "http://www.example.com:8080/?foo=bar"; @@ -194,12 +222,14 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesUrlWithTilde() throws Exception { String url = "http://www.example.com:8080/~user/?foo=bar"; assertTrue("Should match URL with tilde", Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesProtocolCaseInsensitive() throws Exception { String url = "hTtP://android.com"; @@ -207,6 +237,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesDomainNameWithDash() throws Exception { String url = "http://a-nd.r-oid.com"; @@ -218,6 +249,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesDomainNameWithUnderscore() throws Exception { String url = "http://a_nd.r_oid.com"; @@ -229,6 +261,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesPathAndQueryWithDollarSign() throws Exception { String url = "http://android.com/path$?v=$val"; @@ -240,6 +273,7 @@ public class PatternsTest extends TestCase { Patterns.WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testWebUrl_matchesEmptyPathWithQueryParams() throws Exception { String url = "http://android.com?q=v"; @@ -261,6 +295,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.AUTOLINK_WEB_URL + @Test @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception { String url = "http://www.android.com"; @@ -268,6 +303,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithSchemeHostnameAndNewTld() throws Exception { String url = "http://www.android.me"; @@ -275,6 +311,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithHostnameAndNewTld() throws Exception { String url = "android.me"; @@ -286,6 +323,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesChinesePunycodeUrlWithProtocol() throws Exception { String url = "http://xn--fsqu00a.xn--0zwm56d"; @@ -293,6 +331,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesChinesePunycodeUrlWithoutProtocol() throws Exception { String url = "xn--fsqu00a.xn--0zwm56d"; @@ -300,6 +339,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesArabicPunycodeUrlWithProtocol() throws Exception { String url = "http://xn--4gbrim.xn--rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx"; @@ -307,6 +347,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesArabicPunycodeUrlWithoutProtocol() throws Exception { String url = "xn--4gbrim.xn--rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx"; @@ -314,6 +355,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchPunycodeTldThatStartsWithDash() throws Exception { String url = "http://xn--fsqu00a.-xn--0zwm56d"; @@ -321,6 +363,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchPunycodeTldThatEndsWithDash() throws Exception { String url = "http://xn--fsqu00a.xn--0zwm56d-"; @@ -328,6 +371,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlWithUnicodeDomainName() throws Exception { String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -339,6 +383,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlWithUnicodeTld() throws Exception { String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D"; @@ -346,6 +391,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlWithUnicodePath() throws Exception { String url = "http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" + @@ -354,13 +400,15 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchValidUrlWithInvalidProtocol() throws Exception { - String url = "ftp://www.example.com"; + String url = "invalid://www.example.com"; assertFalse("Should not match URL with invalid protocol", Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithPort() throws Exception { String url = "http://www.example.com:8080"; @@ -368,6 +416,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlWithPortAndQuery() throws Exception { String url = "http://www.example.com:8080/?foo=bar"; @@ -375,6 +424,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlWithTilde() throws Exception { String url = "http://www.example.com:8080/~user/?foo=bar"; @@ -382,6 +432,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesProtocolCaseInsensitive() throws Exception { String url = "hTtP://android.com"; @@ -389,6 +440,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesUrlStartingWithHttpAndDoesNotHaveTld() throws Exception { String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2"; @@ -396,6 +448,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld() throws Exception { @@ -405,13 +458,15 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotPartiallyMatchUnknownProtocol() throws Exception { - String url = "ftp://foo.bar/baz"; + String url = "invalid://foo.bar/baz"; assertFalse("Should not partially match URL with unknown protocol", Patterns.AUTOLINK_WEB_URL.matcher(url).find()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesValidUrlWithEmoji() throws Exception { String url = "Thank\u263A.com"; @@ -419,6 +474,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld() throws Exception { @@ -427,6 +483,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchEmailAddress() throws Exception { @@ -435,6 +492,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesDomainNameWithSurrogatePairs() throws Exception { String url = "android\uD83C\uDF38.com"; @@ -442,6 +500,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesTldWithSurrogatePairs() throws Exception { String url = "http://android.\uD83C\uDF38com"; @@ -449,6 +508,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesPathWithSurrogatePairs() throws Exception { String url = "http://android.com/path-with-\uD83C\uDF38?v=\uD83C\uDF38"; @@ -456,6 +516,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchUrlWithExcludedSurrogate() throws Exception { String url = "http://android\uD83F\uDFFE.com"; @@ -463,6 +524,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_doesNotMatchUnicodeSpaces() throws Exception { String part1 = "http://and"; @@ -493,6 +555,7 @@ public class PatternsTest extends TestCase { } } + @Test @SmallTest public void testAutoLinkWebUrl_matchesDomainNameWithDash() throws Exception { String url = "http://a-nd.r-oid.com"; @@ -504,6 +567,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesDomainNameWithUnderscore() throws Exception { String url = "http://a_nd.r_oid.com"; @@ -515,6 +579,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesPathAndQueryWithDollarSign() throws Exception { String url = "http://android.com/path$?v=$val"; @@ -526,6 +591,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testAutoLinkWebUrl_matchesEmptyPathWithQueryParams() throws Exception { String url = "http://android.com?q=v"; @@ -547,6 +613,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.IP_ADDRESS + @Test @SmallTest public void testIpPattern() throws Exception { boolean t; @@ -560,6 +627,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.DOMAIN_NAME + @Test @SmallTest public void testDomain_matchesPunycodeTld() throws Exception { String domain = "xn--fsqu00a.xn--0zwm56d"; @@ -567,6 +635,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_doesNotMatchPunycodeThatStartsWithDash() throws Exception { String domain = "xn--fsqu00a.-xn--0zwm56d"; @@ -574,6 +643,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_doesNotMatchPunycodeThatEndsWithDash() throws Exception { String domain = "xn--fsqu00a.xn--0zwm56d-"; @@ -581,6 +651,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_doesNotMatchPunycodeLongerThanAllowed() throws Exception { String tld = "xn--"; @@ -592,6 +663,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesObsoleteTld() throws Exception { String domain = "test.yu"; @@ -599,6 +671,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesWithSubDomain() throws Exception { String domain = "mail.example.com"; @@ -606,6 +679,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesWithoutSubDomain() throws Exception { String domain = "android.me"; @@ -613,6 +687,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesUnicodeDomainNames() throws Exception { String domain = "\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -620,6 +695,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_doesNotMatchInvalidDomain() throws Exception { String domain = "__+&42.xer"; @@ -627,6 +703,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesPunycodeArabicDomainName() throws Exception { String domain = "xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c"; @@ -634,6 +711,7 @@ public class PatternsTest extends TestCase { Patterns.DOMAIN_NAME.matcher(domain).matches()); } + @Test @SmallTest public void testDomain_matchesDomainNameWithDash() throws Exception { String url = "http://a-nd.r-oid.com"; @@ -645,6 +723,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_WEB_URL.matcher(url).matches()); } + @Test @SmallTest public void testDomain_matchesDomainNameWithUnderscore() throws Exception { String url = "http://a_nd.r_oid.com"; @@ -658,6 +737,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.AUTOLINK_EMAIL_ADDRESS + @Test @SmallTest public void testAutoLinkEmailAddress_matchesShortValidEmail() throws Exception { String email = "a@a.co"; @@ -665,6 +745,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesRegularEmail() throws Exception { String email = "email@android.com"; @@ -672,6 +753,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesEmailWithMultipleSubdomains() throws Exception { String email = "email@e.somelongdomainnameforandroid.abc.uk"; @@ -679,6 +761,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithDot() throws Exception { String email = "e.mail@android.com"; @@ -686,6 +769,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithPlus() throws Exception { String email = "e+mail@android.com"; @@ -693,6 +777,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithUnderscore() throws Exception { String email = "e_mail@android.com"; @@ -700,6 +785,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithDash() throws Exception { String email = "e-mail@android.com"; @@ -707,6 +793,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithApostrophe() throws Exception { String email = "e'mail@android.com"; @@ -714,6 +801,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithDigits() throws Exception { String email = "123@android.com"; @@ -721,6 +809,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesUnicodeLocalPart() throws Exception { String email = "\uD604\uAE08\uC601\uC218\uC99D@android.kr"; @@ -728,6 +817,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithEmoji() throws Exception { String email = "smiley\u263A@android.com"; @@ -735,6 +825,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartWithSurrogatePairs() throws Exception { String email = "\uD83C\uDF38@android.com"; @@ -742,6 +833,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesDomainWithDash() throws Exception { String email = "email@an-droid.com"; @@ -749,6 +841,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesUnicodeDomain() throws Exception { String email = "email@\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -756,6 +849,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesUnicodeLocalPartAndDomain() throws Exception { String email = "\uD604\uAE08\uC601\uC218\uC99D@\uD604\uAE08\uC601\uC218\uC99D.kr"; @@ -763,6 +857,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesDomainWithEmoji() throws Exception { String email = "smiley@\u263Aandroid.com"; @@ -770,6 +865,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesDomainWithSurrogatePairs() throws Exception { String email = "email@\uD83C\uDF38android.com"; @@ -777,6 +873,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartAndDomainWithSurrogatePairs() throws Exception { @@ -785,6 +882,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchStringWithoutAtSign() throws Exception { String email = "android.com"; @@ -792,6 +890,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchPlainString() throws Exception { String email = "email"; @@ -799,6 +898,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchStringWithMultipleAtSigns() throws Exception { String email = "email@android@android.com"; @@ -806,6 +906,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchEmailWithoutTld() throws Exception { String email = "email@android"; @@ -813,6 +914,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchLocalPartEndingWithDot() throws Exception { String email = "email.@android.com"; @@ -820,6 +922,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchLocalPartStartingWithDot() throws Exception { String email = ".email@android.com"; @@ -827,6 +930,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchDomainStartingWithDash() throws Exception { String email = "email@-android.com"; @@ -834,6 +938,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchDomainWithConsecutiveDots() throws Exception { String email = "email@android..com"; @@ -841,6 +946,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchEmailWithIpAsDomain() throws Exception { String email = "email@127.0.0.1"; @@ -848,6 +954,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_doesNotMatchEmailWithInvalidTld() throws Exception { String email = "email@android.c"; @@ -855,6 +962,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesLocalPartUpTo64Chars() throws Exception { String localPart = ""; @@ -871,6 +979,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesSubdomainUpTo63Chars() throws Exception { String subdomain = ""; @@ -888,6 +997,7 @@ public class PatternsTest extends TestCase { Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(email).matches()); } + @Test @SmallTest public void testAutoLinkEmailAddress_matchesDomainUpTo255Chars() throws Exception { String longDomain = ""; @@ -909,6 +1019,7 @@ public class PatternsTest extends TestCase { // Tests for Patterns.PHONE + @Test @SmallTest public void testPhonePattern() throws Exception { boolean t; diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java index caa12083fafb..32548b4cff19 100644 --- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java +++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java @@ -16,9 +16,17 @@ package android.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.time.Clock; import java.time.Instant; @@ -28,19 +36,18 @@ import java.time.ZonedDateTime; import java.util.Iterator; @SmallTest -public class RecurrenceRuleTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class RecurrenceRuleTest { static Clock sOriginalClock; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { sOriginalClock = RecurrenceRule.sClock; } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { RecurrenceRule.sClock = sOriginalClock; } @@ -48,6 +55,7 @@ public class RecurrenceRuleTest extends TestCase { RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault()); } + @Test public void testSimpleMonth() throws Exception { setClock(Instant.parse("2015-11-20T10:15:30.00Z")); final RecurrenceRule r = new RecurrenceRule( @@ -68,6 +76,7 @@ public class RecurrenceRuleTest extends TestCase { ZonedDateTime.parse("2015-11-14T00:00:00.00Z")), it.next()); } + @Test public void testSimpleDays() throws Exception { setClock(Instant.parse("2015-01-01T10:15:30.00Z")); final RecurrenceRule r = new RecurrenceRule( @@ -89,6 +98,7 @@ public class RecurrenceRuleTest extends TestCase { assertFalse(it.hasNext()); } + @Test public void testNotRecurring() throws Exception { setClock(Instant.parse("2015-01-01T10:15:30.00Z")); final RecurrenceRule r = new RecurrenceRule( @@ -106,6 +116,7 @@ public class RecurrenceRuleTest extends TestCase { assertFalse(it.hasNext()); } + @Test public void testNever() throws Exception { setClock(Instant.parse("2015-01-01T10:15:30.00Z")); final RecurrenceRule r = RecurrenceRule.buildNever(); @@ -116,6 +127,7 @@ public class RecurrenceRuleTest extends TestCase { assertFalse(it.hasNext()); } + @Test public void testSane() throws Exception { final RecurrenceRule r = new RecurrenceRule( ZonedDateTime.parse("1980-01-31T00:00:00.000Z"), diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java index 3c17486aeb94..1df1090e0343 100644 --- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java +++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java @@ -17,9 +17,13 @@ package android.util; import static com.google.common.truth.Truth.assertThat; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +33,9 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class SparseSetArrayTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test public void testAddAll() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); @@ -51,6 +58,7 @@ public class SparseSetArrayTest { } @Test + @IgnoreUnderRavenwood(reason = "b/315036461") public void testCopyConstructor() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java index b5d62704ba29..dfd1523465ae 100644 --- a/core/tests/coretests/src/android/util/StateSetTest.java +++ b/core/tests/coretests/src/android/util/StateSetTest.java @@ -16,16 +16,29 @@ package android.util; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Tests for {@link StateSet} */ +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = StateSet.class) +public class StateSetTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); -public class StateSetTest extends TestCase { - + @Test @SmallTest public void testStateSetPositiveMatches() throws Exception { int[] stateSpec = new int[2]; @@ -48,6 +61,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, stateSet)); } + @Test @SmallTest public void testStatesSetMatchMixEmUp() throws Exception { int[] stateSpec = new int[2]; @@ -70,6 +84,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, stateSet)); } + @Test @SmallTest public void testStateSetNegativeMatches() throws Exception { int[] stateSpec = new int[2]; @@ -92,6 +107,7 @@ public class StateSetTest extends TestCase { assertFalse(StateSet.stateSetMatches(stateSpec, stateSet)); } + @Test @SmallTest public void testEmptySetMatchesNegtives() throws Exception { int[] stateSpec = {-12345, -6789}; @@ -101,6 +117,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, stateSet2)); } + @Test @SmallTest public void testEmptySetFailsPositives() throws Exception { int[] stateSpec = {12345}; @@ -110,6 +127,7 @@ public class StateSetTest extends TestCase { assertFalse(StateSet.stateSetMatches(stateSpec, stateSet2)); } + @Test @SmallTest public void testEmptySetMatchesWildcard() throws Exception { int[] stateSpec = StateSet.WILD_CARD; @@ -119,6 +137,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, stateSet2)); } + @Test @SmallTest public void testSingleStatePositiveMatches() throws Exception { int[] stateSpec = new int[2]; @@ -135,6 +154,7 @@ public class StateSetTest extends TestCase { assertFalse(StateSet.stateSetMatches(stateSpec, state)); } + @Test @SmallTest public void testSingleStateNegativeMatches() throws Exception { int[] stateSpec = new int[2]; @@ -151,6 +171,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, state)); } + @Test @SmallTest public void testZeroStateOnlyMatchesDefault() throws Exception { int[] stateSpec = new int[3]; @@ -166,6 +187,7 @@ public class StateSetTest extends TestCase { assertTrue(StateSet.stateSetMatches(stateSpec, state)); } + @Test @SmallTest public void testNullStateOnlyMatchesDefault() throws Exception { int[] stateSpec = new int[3]; diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java index e0c583ddc978..48e76f79a928 100644 --- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java +++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java @@ -26,12 +26,16 @@ import static org.junit.Assert.assertTrue; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import android.content.Context; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.core.app.ApplicationProvider; import libcore.io.Streams; import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -48,13 +52,21 @@ import java.util.zip.ZipFile; /** Unit test for {@link android.util.apk.SourceStampVerifier} */ @RunWith(JUnit4.class) +@IgnoreUnderRavenwood(blockedBy = SourceStampVerifier.class) public class SourceStampVerifierTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); - private final Context mContext = ApplicationProvider.getApplicationContext(); + private Context mContext; private File mPrimaryApk; private File mSecondaryApk; + @Before + public void setUp() throws Exception { + mContext = ApplicationProvider.getApplicationContext(); + } + @After public void tearDown() throws Exception { if (mPrimaryApk != null) { diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 617262203c6b..b30a0c8fbbd3 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -472,6 +472,7 @@ public class ViewRootImplTest { * Test the value of the frame rate cateogry based on the visibility of a view * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE * Visible: FRAME_RATE_CATEGORY_NORMAL + * Also, mIsFrameRateBoosting should be true when the visibility becomes visible */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) @@ -485,6 +486,7 @@ public class ViewRootImplTest { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); }); + sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { view.setVisibility(View.VISIBLE); @@ -492,6 +494,11 @@ public class ViewRootImplTest { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); + sInstrumentation.waitForIdleSync(); + + sInstrumentation.runOnMainSync(() -> { + assertEquals(viewRootImpl.getIsFrameRateBoosting(), true); + }); } /** diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java index 7bef55e23919..909af7b4c5fb 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java @@ -26,6 +26,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Bundle; import android.os.Parcel; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -33,6 +34,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +42,10 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class InputMethodInfoTest { + @Rule + public SetFlagsRule mSetFlagsRule = + new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); + @Test public void testEqualsAndHashCode() throws Exception { final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta); @@ -111,6 +117,19 @@ public class InputMethodInfoTest { assertThat(clone.isVrOnly(), is(true)); } + @Test + public void testIsVirtualDeviceOnly() throws Exception { + mSetFlagsRule.enableFlags(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME); + + final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_virtual_device_only); + + assertThat(imi.isVirtualDeviceOnly(), is(true)); + + final InputMethodInfo clone = cloneViaParcel(imi); + + assertThat(clone.isVirtualDeviceOnly(), is(true)); + } + private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes) throws Exception { final Context context = InstrumentationRegistry.getContext(); diff --git a/core/tests/coretests/src/android/view/menu/MenuScenario.java b/core/tests/coretests/src/android/view/menu/MenuScenario.java index 668aec4eb18f..a1615afe4fa3 100644 --- a/core/tests/coretests/src/android/view/menu/MenuScenario.java +++ b/core/tests/coretests/src/android/view/menu/MenuScenario.java @@ -18,7 +18,7 @@ package android.view.menu; import android.app.Activity; import android.os.Bundle; -import android.util.ListScenario; +import android.widget.ListScenario; import android.util.SparseArray; import android.view.Menu; import android.view.MenuItem; diff --git a/core/tests/coretests/src/android/util/GridScenario.java b/core/tests/coretests/src/android/widget/GridScenario.java index e7ee1cd59c7c..d74080863895 100644 --- a/core/tests/coretests/src/android/util/GridScenario.java +++ b/core/tests/coretests/src/android/widget/GridScenario.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.util; +package android.widget; import android.app.Activity; import android.os.Bundle; diff --git a/core/tests/coretests/src/android/util/InternalSelectionView.java b/core/tests/coretests/src/android/widget/InternalSelectionView.java index 4a1baef8077a..f2d318226540 100644 --- a/core/tests/coretests/src/android/util/InternalSelectionView.java +++ b/core/tests/coretests/src/android/widget/InternalSelectionView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.util; +package android.widget; import android.content.Context; import android.content.res.TypedArray; @@ -22,6 +22,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; +import android.util.AttributeSet; import android.view.KeyEvent; import android.view.View; diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/widget/ListScenario.java index 74dc4b4b34a1..589d90d82285 100644 --- a/core/tests/coretests/src/android/util/ListScenario.java +++ b/core/tests/coretests/src/android/widget/ListScenario.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,15 @@ * limitations under the License. */ -package android.util; +package android.widget; import android.app.Activity; import android.graphics.Rect; import android.os.Bundle; +import android.util.ListItemFactory; import android.view.View; import android.view.ViewGroup; import android.view.Window; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; import com.google.android.collect.Maps; diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/widget/ScrollViewScenario.java index ab1a642a9327..ff2ab05d645f 100644 --- a/core/tests/coretests/src/android/util/ScrollViewScenario.java +++ b/core/tests/coretests/src/android/widget/ScrollViewScenario.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.util; +package android.widget; import android.app.Activity; import android.content.Context; @@ -23,6 +23,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.Button; +import android.widget.InternalSelectionView; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; diff --git a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java index 85a4509c18a3..7e2f293b3a99 100644 --- a/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java +++ b/core/tests/coretests/src/android/widget/focus/AdjacentVerticalRectLists.java @@ -18,7 +18,7 @@ package android.widget.focus; import android.app.Activity; import android.os.Bundle; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.ViewGroup; import android.widget.LinearLayout; @@ -30,7 +30,7 @@ import android.widget.LinearLayout; * rectangle of the previously focused view. The view taking focus can use this * to set an internal selection more appropriate using this rect. * - * This Activity excercises that behavior using three adjacent {@link android.util.InternalSelectionView} + * This Activity excercises that behavior using three adjacent {@link InternalSelectionView} * that report interesting rects when giving up focus, and use interesting rects * when taking focus to best select the internal row to show as selected. */ diff --git a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java index 26dc23398366..16a0b35c2dd5 100644 --- a/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java +++ b/core/tests/coretests/src/android/widget/focus/FocusChangeWithInterestingRectHintTest.java @@ -17,7 +17,7 @@ package android.widget.focus; import android.test.ActivityInstrumentationTestCase; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.KeyEvent; import androidx.test.filters.LargeTest; @@ -31,7 +31,7 @@ import androidx.test.filters.MediumTest; * rectangle of the previously focused view. The view taking focus can use this * to set an internal selection more appropriate using this rect. * - * This tests that behavior using three adjacent {@link android.util.InternalSelectionView} + * This tests that behavior using three adjacent {@link InternalSelectionView} * that report interesting rects when giving up focus, and use interesting rects * when taking focus to best select the internal row to show as selected. * diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java index c5e69b67862f..4231b40431f1 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java @@ -18,7 +18,7 @@ package android.widget.focus; import android.app.Activity; import android.os.Bundle; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; diff --git a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java index e6e76ccc2caf..46190c4ce7c7 100644 --- a/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java +++ b/core/tests/coretests/src/android/widget/focus/ScrollingThroughListOfFocusablesTest.java @@ -18,7 +18,7 @@ package android.widget.focus; import android.graphics.Rect; import android.test.InstrumentationTestCase; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.KeyEvent; import android.widget.ListView; diff --git a/core/tests/coretests/src/android/widget/gridview/GridDelete.java b/core/tests/coretests/src/android/widget/gridview/GridDelete.java index b040f69545f1..5f4326cd7758 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridDelete.java +++ b/core/tests/coretests/src/android/widget/gridview/GridDelete.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelection.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelection.java index a3cda3b1f030..140dd7b696fd 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelection.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelection.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic stacking from top scenario, nothing fancy. Items do not diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java index db4f2dc224fc..1428fea9fc87 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionBaseTest.java @@ -18,7 +18,7 @@ package android.widget.gridview; import android.test.ActivityInstrumentationTestCase; import android.test.ViewAsserts; -import android.util.GridScenario; +import android.widget.GridScenario; import android.widget.GridView; import androidx.test.filters.MediumTest; diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionMany.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionMany.java index a6d481f8a9fd..71766195fcc7 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionMany.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionMany.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic stacking from top scenario, nothing fancy. Items do diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottom.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottom.java index dfcd5fc09cc2..01bb2be37fb8 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottom.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottom.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic stacking from bottom scenario, nothing fancy. Items do not diff --git a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomMany.java b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomMany.java index 26a567e90cef..d79ee880ec97 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomMany.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSetSelectionStackFromBottomMany.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic stacking from bottom scenario, nothing fancy. Items do diff --git a/core/tests/coretests/src/android/widget/gridview/GridSimple.java b/core/tests/coretests/src/android/widget/gridview/GridSimple.java index 67bb7511122a..6ca7eaa14f40 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSimple.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSimple.java @@ -18,7 +18,7 @@ package android.widget.gridview; import android.graphics.drawable.PaintDrawable; import android.os.Bundle; -import android.util.GridScenario; +import android.widget.GridScenario; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; diff --git a/core/tests/coretests/src/android/widget/gridview/GridSingleColumn.java b/core/tests/coretests/src/android/widget/gridview/GridSingleColumn.java index 566e71b7e14f..aaed9ba2789f 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridSingleColumn.java +++ b/core/tests/coretests/src/android/widget/gridview/GridSingleColumn.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; import android.widget.GridView; /** diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottom.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottom.java index 2f0a88fc9767..457b00ddce54 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottom.java +++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottom.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic bottom stacking from bottom scenario, nothing fancy. Items do not diff --git a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomMany.java b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomMany.java index 33a9592fdc9b..9029bc57ac85 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomMany.java +++ b/core/tests/coretests/src/android/widget/gridview/GridStackFromBottomMany.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * Basic bottom stacking from bottom scenario, nothing fancy. The grid items do not fit on the diff --git a/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacing.java b/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacing.java index 0d01d3087d9d..6aa864f756fc 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacing.java +++ b/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacing.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * A grid with vertical spacing between rows diff --git a/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacingStackFromBottom.java b/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacingStackFromBottom.java index bd686809dba7..25ef6820c8b5 100644 --- a/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacingStackFromBottom.java +++ b/core/tests/coretests/src/android/widget/gridview/GridVerticalSpacingStackFromBottom.java @@ -16,7 +16,7 @@ package android.widget.gridview; -import android.util.GridScenario; +import android.widget.GridScenario; /** * A grid with vertical spacing between rows that stacks from the bottom diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java index 62b93d6d208a..1e57d69edec6 100644 --- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java +++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java @@ -18,7 +18,7 @@ package android.widget.listview; import android.app.Activity; import android.os.Bundle; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java index cd76d70acdaf..5d319c5f4930 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravity.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic bottom gravity scenario, nothing fancy. Items do not diff --git a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java index e048e3eb98a0..331fd121370c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java +++ b/core/tests/coretests/src/android/widget/listview/ListBottomGravityMany.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic bottom gravity scenario, nothing fancy. There are diff --git a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java index 86407411c2d0..bc7c19f34fd9 100644 --- a/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java +++ b/core/tests/coretests/src/android/widget/listview/ListButtonsDiagonalAcrossItems.java @@ -19,7 +19,7 @@ package android.widget.listview; import static android.util.ListItemFactory.Slot; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListEndingWithMultipleSeparators.java b/core/tests/coretests/src/android/widget/listview/ListEndingWithMultipleSeparators.java index 85f9924a16aa..834f0d1ec01c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListEndingWithMultipleSeparators.java +++ b/core/tests/coretests/src/android/widget/listview/ListEndingWithMultipleSeparators.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListEndingWithMultipleSeparators extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListGetSelectedView.java b/core/tests/coretests/src/android/widget/listview/ListGetSelectedView.java index 5639195198f1..abd5d2e5dab4 100644 --- a/core/tests/coretests/src/android/widget/listview/ListGetSelectedView.java +++ b/core/tests/coretests/src/android/widget/listview/ListGetSelectedView.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic top gravity scenario. This test is made to check that getSelectedView() will return diff --git a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java index 74eda3b850b9..fd24d61d4327 100644 --- a/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java +++ b/core/tests/coretests/src/android/widget/listview/ListHeterogeneous.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java index e98de9c1204c..7ce5ee3d2f81 100644 --- a/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java +++ b/core/tests/coretests/src/android/widget/listview/ListHorizontalFocusWithinItemWins.java @@ -20,7 +20,7 @@ import static android.util.ListItemFactory.Slot; import android.content.Context; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java index 0ec7a2431b93..b613741a9c62 100644 --- a/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java +++ b/core/tests/coretests/src/android/widget/listview/ListInterleaveFocusables.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java index 3159e53eabc5..dc40f5f5d98b 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusableAboveUnfocusable.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java index 861e2a919052..d21d234c92aa 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesClose.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java index e9c9c1dd586c..0bb080b071a1 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemFocusablesFarApart.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.util.ListItemFactory; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java index 2a0e013be616..cbe07cc475f0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemISVAndButton.java @@ -17,8 +17,8 @@ package android.widget.listview; import android.content.Context; -import android.util.InternalSelectionView; -import android.util.ListScenario; +import android.widget.InternalSelectionView; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java index d80fd90de53e..240119e947d6 100644 --- a/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java +++ b/core/tests/coretests/src/android/widget/listview/ListItemsExpandOnSelection.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.content.Context; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; diff --git a/core/tests/coretests/src/android/widget/listview/ListLastItemPartiallyVisible.java b/core/tests/coretests/src/android/widget/listview/ListLastItemPartiallyVisible.java index d7337497a443..48c70d6cd84c 100644 --- a/core/tests/coretests/src/android/widget/listview/ListLastItemPartiallyVisible.java +++ b/core/tests/coretests/src/android/widget/listview/ListLastItemPartiallyVisible.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * A list where the very last item is partially visible, but still requires scrolling diff --git a/core/tests/coretests/src/android/widget/listview/ListOfItemsShorterThanScreen.java b/core/tests/coretests/src/android/widget/listview/ListOfItemsShorterThanScreen.java index 46decfa2e578..55af8d47c803 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfItemsShorterThanScreen.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfItemsShorterThanScreen.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListOfItemsShorterThanScreen extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListOfItemsTallerThanScreen.java b/core/tests/coretests/src/android/widget/listview/ListOfItemsTallerThanScreen.java index 0d8899371085..2b619e95f5dc 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfItemsTallerThanScreen.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfItemsTallerThanScreen.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListOfItemsTallerThanScreen extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListOfShortShortTallShortShort.java b/core/tests/coretests/src/android/widget/listview/ListOfShortShortTallShortShort.java index 1639aa457cac..09f56b38578f 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfShortShortTallShortShort.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfShortShortTallShortShort.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Exposes fading in and out multiple items. diff --git a/core/tests/coretests/src/android/widget/listview/ListOfShortTallShort.java b/core/tests/coretests/src/android/widget/listview/ListOfShortTallShort.java index 960e1297cb22..8ad8fff735fe 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfShortTallShort.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfShortTallShort.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Two short items separated by one that is taller than the screen. diff --git a/core/tests/coretests/src/android/widget/listview/ListOfThinItems.java b/core/tests/coretests/src/android/widget/listview/ListOfThinItems.java index 007479f01a77..3f703afea752 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfThinItems.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfThinItems.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListOfThinItems extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java index 70b9081128a0..f1dcdad20e89 100644 --- a/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java +++ b/core/tests/coretests/src/android/widget/listview/ListOfTouchables.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java index af8e899a867a..a107117250b0 100644 --- a/core/tests/coretests/src/android/widget/listview/ListSetSelection.java +++ b/core/tests/coretests/src/android/widget/listview/ListSetSelection.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.os.Bundle; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.KeyEvent; import android.view.View; import android.widget.Button; diff --git a/core/tests/coretests/src/android/widget/listview/ListSimple.java b/core/tests/coretests/src/android/widget/listview/ListSimple.java index f53638eb8ff1..9b3a98fe44d6 100644 --- a/core/tests/coretests/src/android/widget/listview/ListSimple.java +++ b/core/tests/coretests/src/android/widget/listview/ListSimple.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.os.Bundle; -import android.util.ListScenario; +import android.widget.ListScenario; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; diff --git a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java index 31339e9ee9c2..67ef6d15538e 100644 --- a/core/tests/coretests/src/android/widget/listview/ListTopGravity.java +++ b/core/tests/coretests/src/android/widget/listview/ListTopGravity.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic top gravity scenario, nothing fancy. Items do not diff --git a/core/tests/coretests/src/android/widget/listview/ListTopGravityMany.java b/core/tests/coretests/src/android/widget/listview/ListTopGravityMany.java index 5592ad9f6d20..9618c81de5e1 100644 --- a/core/tests/coretests/src/android/widget/listview/ListTopGravityMany.java +++ b/core/tests/coretests/src/android/widget/listview/ListTopGravityMany.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic top gravity scenario, nothing fancy. There are diff --git a/core/tests/coretests/src/android/widget/listview/ListWithEditTextHeader.java b/core/tests/coretests/src/android/widget/listview/ListWithEditTextHeader.java index 5303faf8d320..1a204a24b9e3 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithEditTextHeader.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithEditTextHeader.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * A list view with a single edit text in a header. diff --git a/core/tests/coretests/src/android/widget/listview/ListWithFirstScreenUnSelectable.java b/core/tests/coretests/src/android/widget/listview/ListWithFirstScreenUnSelectable.java index 526128371375..d6e7c6fe6c9e 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithFirstScreenUnSelectable.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithFirstScreenUnSelectable.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * The first item is unselectable, and takes up the whole screen. diff --git a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java index 6030582a2445..fa2832edf614 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithHeaders.java @@ -17,7 +17,7 @@ package android.widget.listview; import android.os.Bundle; -import android.util.ListScenario; +import android.widget.ListScenario; import android.widget.Button; import android.widget.ListAdapter; import android.widget.ListView; diff --git a/core/tests/coretests/src/android/widget/listview/ListWithNoFadingEdge.java b/core/tests/coretests/src/android/widget/listview/ListWithNoFadingEdge.java index b870fc855646..f8b4f786820a 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithNoFadingEdge.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithNoFadingEdge.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListWithNoFadingEdge extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListWithOffScreenNextSelectable.java b/core/tests/coretests/src/android/widget/listview/ListWithOffScreenNextSelectable.java index 2e65bd0f2ffe..b8f14d21c1e1 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithOffScreenNextSelectable.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithOffScreenNextSelectable.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Pressing down from position 0 requires looking past positions 1, 2 and 3 to diff --git a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java index 13e770cd4e47..afc4c3b754c7 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithOnItemSelectedAction.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; import android.widget.TextView; /** diff --git a/core/tests/coretests/src/android/widget/listview/ListWithScreenOfNoSelectables.java b/core/tests/coretests/src/android/widget/listview/ListWithScreenOfNoSelectables.java index 108ac4d9e6f6..fe33bf05acb3 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithScreenOfNoSelectables.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithScreenOfNoSelectables.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; public class ListWithScreenOfNoSelectables extends ListScenario { diff --git a/core/tests/coretests/src/android/widget/listview/ListWithSeparators.java b/core/tests/coretests/src/android/widget/listview/ListWithSeparators.java index 0f4f2d88a41b..1a3955a4b1cb 100644 --- a/core/tests/coretests/src/android/widget/listview/ListWithSeparators.java +++ b/core/tests/coretests/src/android/widget/listview/ListWithSeparators.java @@ -16,7 +16,7 @@ package android.widget.listview; -import android.util.ListScenario; +import android.widget.ListScenario; /** * Basic separator scenario, nothing fancy. diff --git a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java index e9baabf3175a..09c140ee96d9 100644 --- a/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java +++ b/core/tests/coretests/src/android/widget/listview/focus/AdjacentListsWithAdjacentISVsInsideTest.java @@ -17,7 +17,7 @@ package android.widget.listview.focus; import android.test.ActivityInstrumentationTestCase; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.KeyEvent; import android.widget.ListView; import android.widget.listview.AdjacentListsWithAdjacentISVsInside; diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java index a30985bfaf87..30f5c67f9f4e 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionView.java @@ -16,8 +16,8 @@ package android.widget.scroll; -import android.util.InternalSelectionView; -import android.util.ScrollViewScenario; +import android.widget.InternalSelectionView; +import android.widget.ScrollViewScenario; import android.widget.Button; /** diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java index 825aa1aa85a3..d977ab4c7efa 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonAboveTallInternalSelectionViewTest.java @@ -17,7 +17,7 @@ package android.widget.scroll; import android.test.ActivityInstrumentationTestCase; -import android.util.InternalSelectionView; +import android.widget.InternalSelectionView; import android.view.KeyEvent; import androidx.test.filters.MediumTest; diff --git a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java index 47d36ddf9be1..5149fd43bc65 100644 --- a/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java +++ b/core/tests/coretests/src/android/widget/scroll/ButtonsWithTallTextViewInBetween.java @@ -16,7 +16,7 @@ package android.widget.scroll; -import android.util.ScrollViewScenario; +import android.widget.ScrollViewScenario; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; diff --git a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java index 90ede7d35511..01d3b53ba062 100644 --- a/core/tests/coretests/src/android/widget/scroll/ShortButtons.java +++ b/core/tests/coretests/src/android/widget/scroll/ShortButtons.java @@ -16,7 +16,7 @@ package android.widget.scroll; -import android.util.ScrollViewScenario; +import android.widget.ScrollViewScenario; import android.widget.Button; import android.widget.LinearLayout; diff --git a/core/tests/coretests/src/android/widget/scroll/TallTextAboveButton.java b/core/tests/coretests/src/android/widget/scroll/TallTextAboveButton.java index 4096fe9c1c2e..bf800c0d09d7 100644 --- a/core/tests/coretests/src/android/widget/scroll/TallTextAboveButton.java +++ b/core/tests/coretests/src/android/widget/scroll/TallTextAboveButton.java @@ -16,7 +16,7 @@ package android.widget.scroll; -import android.util.ScrollViewScenario; +import android.widget.ScrollViewScenario; /** * An (unfocusable) text view that takes up more than the height diff --git a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java index 7d5a8d8441e8..6febdf96ebbb 100644 --- a/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java +++ b/core/tests/coretests/src/android/widget/scroll/arrowscroll/MultiPageTextWithPadding.java @@ -16,7 +16,7 @@ package android.widget.scroll.arrowscroll; -import android.util.ScrollViewScenario; +import android.widget.ScrollViewScenario; /** * One TextView with a text covering several pages. Padding is added diff --git a/core/tests/coretests/src/com/android/internal/jank/CujTest.java b/core/tests/coretests/src/com/android/internal/jank/CujTest.java new file mode 100644 index 000000000000..bf35ed0a1601 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/jank/CujTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.jank; + +import static android.text.TextUtils.formatSimple; + +import static com.android.internal.jank.Cuj.getNameOfCuj; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import androidx.test.filters.SmallTest; + +import com.android.internal.util.FrameworkStatsLog; + +import com.google.common.truth.Expect; + +import org.junit.Rule; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SmallTest +public class CujTest { + private static final String ENUM_NAME_PREFIX = + "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__"; + private static final Set<String> DEPRECATED_VALUES = new HashSet<>() { + { + add(ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION"); + } + }; + private static final Map<Integer, String> ENUM_NAME_EXCEPTION_MAP = new HashMap<>() { + { + put(Cuj.CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD")); + put(Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR")); + put(Cuj.CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH")); + put(Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR")); + put(Cuj.CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE")); + put(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE")); + put(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE")); + put(Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE")); + put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND")); + put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE")); + put(Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING")); + } + }; + + @Rule + public final Expect mExpect = Expect.create(); + + @Test + public void testCujNameLimit() { + getCujConstants().forEach(f -> { + final int cuj = getIntFieldChecked(f); + mExpect.withMessage(formatSimple("Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj))) + .that(getNameOfCuj(cuj).length()) + .isAtMost(Cuj.MAX_LENGTH_OF_CUJ_NAME); + }); + } + + @Test + public void testCujTypeEnumCorrectlyDefined() throws Exception { + List<Field> cujEnumFields = getCujConstants().toList(); + + HashSet<Integer> allValues = new HashSet<>(); + for (Field field : cujEnumFields) { + int fieldValue = field.getInt(null); + assertWithMessage("All CujType values must be unique. Field %s repeats existing value.", + field.getName()) + .that(allValues.add(fieldValue)) + .isTrue(); + assertWithMessage("Field %s must have a value <= LAST_CUJ", field.getName()) + .that(fieldValue) + .isAtMost(Cuj.LAST_CUJ); + assertWithMessage("Field %s must have a statsd mapping.", field.getName()) + .that(Cuj.logToStatsd(fieldValue)) + .isTrue(); + } + } + + @Test + public void testCujsMapToEnumsCorrectly() { + List<Field> cujs = getCujConstants().toList(); + + Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields()) + .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX) + && !DEPRECATED_VALUES.contains(f.getName()) + && Modifier.isStatic(f.getModifiers()) + && f.getType() == int.class) + .collect(Collectors.toMap(CujTest::getIntFieldChecked, Field::getName)); + + assertThat(enumsMap.size() - 1).isEqualTo(cujs.size()); + + cujs.forEach(f -> { + final int cuj = getIntFieldChecked(f); + final String cujName = f.getName(); + final String expectedEnumName = + ENUM_NAME_EXCEPTION_MAP.getOrDefault(cuj, getEnumName(cujName.substring(4))); + final int enumKey = Cuj.getStatsdInteractionType(cuj); + final String enumName = enumsMap.get(enumKey); + final String expectedNameOfCuj = formatSimple("CUJ_%s", getNameOfCuj(cuj)); + + mExpect.withMessage( + formatSimple("%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey)) + .that(expectedEnumName.equals(enumName)) + .isTrue(); + mExpect.withMessage( + formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s", + cuj, cujName, expectedNameOfCuj)) + .that(cujName.equals(expectedNameOfCuj)) + .isTrue(); + }); + } + + private static String getEnumName(String name) { + return formatSimple("%s%s", ENUM_NAME_PREFIX, name); + } + + private static int getIntFieldChecked(Field field) { + try { + return field.getInt(null); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + private static Stream<Field> getCujConstants() { + return Arrays.stream(Cuj.class.getDeclaredFields()) + .filter(f -> f.getName().startsWith("CUJ_") + && Modifier.isStatic(f.getModifiers()) + && f.getType() == int.class); + } +} diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 1b9717a6dfc5..1a7117e3b4a1 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -22,9 +22,8 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_ import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.jank.FrameTracker.ViewRootWrapper; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION; +import static com.android.internal.jank.Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; +import static com.android.internal.jank.Cuj.CUJ_WALLPAPER_TRANSITION; import static com.android.internal.util.FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED; import static com.google.common.truth.Truth.assertThat; @@ -49,15 +48,14 @@ import android.view.SurfaceControl.OnJankDataListener; import android.view.View; import android.view.ViewAttachTestActivity; +import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.filters.SmallTest; -import androidx.test.rule.ActivityTestRule; import com.android.internal.jank.FrameTracker.ChoreographerWrapper; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; import com.android.internal.jank.FrameTracker.StatsLogWrapper; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.InteractionJankMonitor.Configuration; -import com.android.internal.jank.InteractionJankMonitor.Session; import org.junit.Before; import org.junit.Rule; @@ -69,14 +67,14 @@ import java.util.concurrent.TimeUnit; @SmallTest public class FrameTrackerTest { - private static final String CUJ_POSTFIX = ""; + private static final String SESSION_NAME = "SessionName"; private static final long FRAME_TIME_60Hz = (long) 1e9 / 60; private ViewAttachTestActivity mActivity; @Rule - public ActivityTestRule<ViewAttachTestActivity> mRule = - new ActivityTestRule<>(ViewAttachTestActivity.class); + public ActivityScenarioRule<ViewAttachTestActivity> mRule = + new ActivityScenarioRule<>(ViewAttachTestActivity.class); private ThreadedRendererWrapper mRenderer; private FrameMetricsWrapper mWrapper; @@ -86,12 +84,13 @@ public class FrameTrackerTest { private StatsLogWrapper mStatsLog; private ArgumentCaptor<OnJankDataListener> mListenerCapture; private SurfaceControl mSurfaceControl; + private FrameTracker.FrameTrackerListener mTrackerListener; private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; @Before public void setup() { // Prepare an activity for getting ThreadedRenderer later. - mActivity = mRule.getActivity(); + mRule.getScenario().onActivity(activity -> mActivity = activity); View view = mActivity.getWindow().getDecorView(); assertThat(view.isAttachedToWindow()).isTrue(); @@ -116,36 +115,38 @@ public class FrameTrackerTest { mChoreographer = mock(ChoreographerWrapper.class); mStatsLog = mock(StatsLogWrapper.class); mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); + mTrackerListener = mock(FrameTracker.FrameTrackerListener.class); } - private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) { - InteractionJankMonitor monitor = mock(InteractionJankMonitor.class); - Handler handler = mRule.getActivity().getMainThreadHandler(); - Session session = new Session(cuj, postfix); + private FrameTracker spyFrameTracker(boolean surfaceOnly) { + Handler handler = mActivity.getMainThreadHandler(); Configuration config = mock(Configuration.class); + when(config.getSessionName()).thenReturn(SESSION_NAME); when(config.isSurfaceOnly()).thenReturn(surfaceOnly); when(config.getSurfaceControl()).thenReturn(mSurfaceControl); when(config.shouldDeferMonitor()).thenReturn(true); when(config.getDisplayId()).thenReturn(42); - View view = mRule.getActivity().getWindow().getDecorView(); + View view = mActivity.getWindow().getDecorView(); Handler spyHandler = spy(new Handler(handler.getLooper())); when(config.getView()).thenReturn(surfaceOnly ? null : view); when(config.getHandler()).thenReturn(spyHandler); + when(config.logToStatsd()).thenReturn(true); + when(config.getStatsdInteractionType()).thenReturn(surfaceOnly + ? Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION) + : Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)); FrameTracker frameTracker = Mockito.spy( - new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper, + new FrameTracker(config, mRenderer, mViewRootWrapper, mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog, /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1, - /* FrameTrackerListener= */ null, config)); - doNothing().when(frameTracker).triggerPerfetto(); + mTrackerListener)); doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture()); return frameTracker; } @Test public void testOnlyFirstWindowFrameOverThreshold() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP)) @@ -169,11 +170,11 @@ public class FrameTrackerTest { sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L); verify(tracker).removeObservers(); - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(0L) /* missedFrames */, eq(5000000L) /* maxFrameTimeNanos */, @@ -184,8 +185,7 @@ public class FrameTrackerTest { @Test public void testSfJank() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -206,12 +206,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); // We detected a janky frame - trigger Perfetto - verify(tracker).triggerPerfetto(); + verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(1L) /* missedFrames */, eq(40000000L) /* maxFrameTimeNanos */, @@ -222,8 +222,7 @@ public class FrameTrackerTest { @Test public void testFirstFrameJankyNoTrigger() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -243,13 +242,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); - // We detected a janky frame - trigger Perfetto - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(0L) /* missedFrames */, eq(4000000L) /* maxFrameTimeNanos */, @@ -260,8 +258,7 @@ public class FrameTrackerTest { @Test public void testOtherFrameOverThreshold() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -282,12 +279,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); // We detected a janky frame - trigger Perfetto - verify(tracker).triggerPerfetto(); + verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(1L) /* missedFrames */, eq(40000000L) /* maxFrameTimeNanos */, @@ -298,8 +295,7 @@ public class FrameTrackerTest { @Test public void testLastFrameOverThresholdBeforeEnd() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -323,12 +319,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); // We detected a janky frame - trigger Perfetto - verify(tracker).triggerPerfetto(); + verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(1L) /* missedFrames */, eq(50000000L) /* maxFrameTimeNanos */, @@ -342,8 +338,7 @@ public class FrameTrackerTest { */ @Test public void testNoOvercountingAfterEnd() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -367,11 +362,11 @@ public class FrameTrackerTest { sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L); verify(tracker).removeObservers(); - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]), + eq(Cuj.getStatsdInteractionType(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)), eq(2L) /* totalFrames */, eq(0L) /* missedFrames */, eq(4000000L) /* maxFrameTimeNanos */, @@ -382,8 +377,7 @@ public class FrameTrackerTest { @Test public void testBeginCancel() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -402,13 +396,12 @@ public class FrameTrackerTest { tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); verify(tracker).removeObservers(); // Since the tracker has been cancelled, shouldn't trigger perfetto. - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); } @Test public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -426,13 +419,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); // Should never trigger Perfetto since it is a cancel. - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); } @Test public void testCancelIfEndVsyncIdLessThanBeginVsyncId() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -450,13 +442,12 @@ public class FrameTrackerTest { verify(tracker).removeObservers(); // Should never trigger Perfetto since it is a cancel. - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); } @Test public void testCancelWhenSessionNeverBegun() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); verify(tracker).removeObservers(); @@ -464,8 +455,7 @@ public class FrameTrackerTest { @Test public void testEndWhenSessionNeverBegun() { - FrameTracker tracker = spyFrameTracker( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ false); tracker.end(FrameTracker.REASON_END_NORMAL); verify(tracker).removeObservers(); @@ -473,8 +463,7 @@ public class FrameTrackerTest { @Test public void testSurfaceOnlyOtherFrameJanky() { - FrameTracker tracker = spyFrameTracker( - CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -495,12 +484,12 @@ public class FrameTrackerTest { sendFrame(tracker, JANK_NONE, 103L); verify(mSurfaceControlWrapper).removeJankStatsListener(any()); - verify(tracker).triggerPerfetto(); + verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), + eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)), eq(2L) /* totalFrames */, eq(1L) /* missedFrames */, eq(0L) /* maxFrameTimeNanos */, @@ -511,8 +500,7 @@ public class FrameTrackerTest { @Test public void testSurfaceOnlyFirstFrameJanky() { - FrameTracker tracker = spyFrameTracker( - CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -533,12 +521,12 @@ public class FrameTrackerTest { sendFrame(tracker, JANK_NONE, 103L); verify(mSurfaceControlWrapper).removeJankStatsListener(any()); - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), + eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)), eq(2L) /* totalFrames */, eq(0L) /* missedFrames */, eq(0L) /* maxFrameTimeNanos */, @@ -549,8 +537,7 @@ public class FrameTrackerTest { @Test public void testSurfaceOnlyLastFrameJanky() { - FrameTracker tracker = spyFrameTracker( - CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); @@ -571,12 +558,12 @@ public class FrameTrackerTest { sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L); verify(mSurfaceControlWrapper).removeJankStatsListener(any()); - verify(tracker, never()).triggerPerfetto(); + verify(mTrackerListener, never()).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), + eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)), eq(2L) /* totalFrames */, eq(0L) /* missedFrames */, eq(0L) /* maxFrameTimeNanos */, @@ -587,8 +574,7 @@ public class FrameTrackerTest { @Test public void testMaxSuccessiveMissedFramesCount() { - FrameTracker tracker = spyFrameTracker( - CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); + FrameTracker tracker = spyFrameTracker(/* surfaceOnly= */ true); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); mRunnableArgumentCaptor.getValue().run(); @@ -604,11 +590,11 @@ public class FrameTrackerTest { sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L); sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L); verify(mSurfaceControlWrapper).removeJankStatsListener(any()); - verify(tracker).triggerPerfetto(); + verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), eq(42), /* displayId */ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ), - eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]), + eq(Cuj.getStatsdInteractionType(CUJ_WALLPAPER_TRANSITION)), eq(6L) /* totalFrames */, eq(5L) /* missedFrames */, eq(0L) /* maxFrameTimeNanos */, diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index b61f995724e5..68095e5eb46c 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -20,23 +20,9 @@ import static android.text.TextUtils.formatSimple; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT; import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE; -import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME; -import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj; +import static com.android.internal.jank.InteractionJankMonitor.Configuration.generateSessionName; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -52,12 +38,11 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; import android.provider.DeviceConfig; -import android.util.SparseArray; import android.view.View; import android.view.ViewAttachTestActivity; +import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.filters.SmallTest; -import androidx.test.rule.ActivityTestRule; import com.android.internal.jank.FrameTracker.ChoreographerWrapper; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; @@ -66,101 +51,54 @@ import com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ViewRootWrapper; import com.android.internal.jank.InteractionJankMonitor.Configuration; -import com.android.internal.jank.InteractionJankMonitor.Session; -import com.android.internal.util.FrameworkStatsLog; import com.google.common.truth.Expect; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; @SmallTest public class InteractionJankMonitorTest { - private static final String CUJ_POSTFIX = ""; - private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>(); - private static final String ENUM_NAME_PREFIX = - "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__"; - - private static final ArrayList<String> DEPRECATED_VALUES = new ArrayList<>(); - private ViewAttachTestActivity mActivity; private View mView; + private Handler mHandler; private HandlerThread mWorker; @Rule - public ActivityTestRule<ViewAttachTestActivity> mRule = - new ActivityTestRule<>(ViewAttachTestActivity.class); + public ActivityScenarioRule<ViewAttachTestActivity> mRule = + new ActivityScenarioRule<>(ViewAttachTestActivity.class); @Rule public final Expect mExpect = Expect.create(); - @BeforeClass - public static void initialize() { - ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD")); - ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE")); - ENUM_NAME_EXCEPTION_MAP.put( - CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING")); - DEPRECATED_VALUES.add(ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION"); - } - - private static String getEnumName(String name) { - return formatSimple("%s%s", ENUM_NAME_PREFIX, name); - } - @Before public void setup() { - // Prepare an activity for getting ThreadedRenderer later. - mActivity = mRule.getActivity(); + mRule.getScenario().onActivity(activity -> mActivity = activity); mView = mActivity.getWindow().getDecorView(); assertThat(mView.isAttachedToWindow()).isTrue(); - Handler handler = spy(new Handler(mActivity.getMainLooper())); - doReturn(true).when(handler).sendMessageAtTime(any(), anyLong()); + mHandler = spy(new Handler(mActivity.getMainLooper())); + doReturn(true).when(mHandler).sendMessageAtTime(any(), anyLong()); mWorker = mock(HandlerThread.class); - doReturn(handler).when(mWorker).getThreadHandler(); + doReturn(mHandler).when(mWorker).getThreadHandler(); } @Test public void testBeginEnd() { InteractionJankMonitor monitor = createMockedInteractionJankMonitor(); - FrameTracker tracker = createMockedFrameTracker(monitor, null); - doReturn(tracker).when(monitor).createFrameTracker(any(), any()); + FrameTracker tracker = createMockedFrameTracker(); + doReturn(tracker).when(monitor).createFrameTracker(any()); doNothing().when(tracker).begin(); doReturn(true).when(tracker).end(anyInt()); // Simulate a trace session and see if begin / end are invoked. - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); + assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); verify(tracker).begin(); - assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); + assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); verify(tracker).end(REASON_END_NORMAL); } @@ -172,10 +110,10 @@ public class InteractionJankMonitorTest { propertiesValues.put("enabled", "false"); DeviceConfig.Properties properties = new DeviceConfig.Properties( DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues); - monitor.getPropertiesChangedListener().onPropertiesChanged(properties); + monitor.updateProperties(properties); - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); - assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); } @Test @@ -185,145 +123,57 @@ public class InteractionJankMonitorTest { assertThat(view.isAttachedToWindow()).isFalse(); // Should return false if the view passed in is not attached to window yet. - assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); - assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.begin(view, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.end(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); } @Test public void testBeginTimeout() { ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); InteractionJankMonitor monitor = createMockedInteractionJankMonitor(); - FrameTracker tracker = createMockedFrameTracker(monitor, null); - doReturn(tracker).when(monitor).createFrameTracker(any(), any()); + FrameTracker tracker = createMockedFrameTracker(); + doReturn(tracker).when(monitor).createFrameTracker(any()); doNothing().when(tracker).begin(); doReturn(true).when(tracker).cancel(anyInt()); - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); + assertThat(monitor.begin(mView, Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); verify(tracker).begin(); - verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture()); + verify(monitor).scheduleTimeoutAction(any(), captor.capture()); Runnable runnable = captor.getValue(); assertThat(runnable).isNotNull(); - mWorker.getThreadHandler().removeCallbacks(runnable); runnable.run(); - verify(monitor).cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, REASON_CANCEL_TIMEOUT); + verify(monitor).cancel(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, REASON_CANCEL_TIMEOUT); verify(tracker).cancel(REASON_CANCEL_TIMEOUT); } @Test - public void testCujTypeEnumCorrectlyDefined() throws Exception { - List<Field> cujEnumFields = - Arrays.stream(InteractionJankMonitor.class.getDeclaredFields()) - .filter(field -> field.getName().startsWith("CUJ_") - && Modifier.isStatic(field.getModifiers()) - && field.getType() == int.class) - .collect(Collectors.toList()); - - HashSet<Integer> allValues = new HashSet<>(); - for (Field field : cujEnumFields) { - int fieldValue = field.getInt(null); - assertWithMessage( - "Field %s must have a mapping to a value in CUJ_TO_STATSD_INTERACTION_TYPE", - field.getName()) - .that(fieldValue < CUJ_TO_STATSD_INTERACTION_TYPE.length) - .isTrue(); - assertWithMessage("All CujType values must be unique. Field %s repeats existing value.", - field.getName()) - .that(allValues.add(fieldValue)) - .isTrue(); - } - } - - @Test - public void testCujsMapToEnumsCorrectly() { - List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields()) - .filter(f -> f.getName().startsWith("CUJ_") - && Modifier.isStatic(f.getModifiers()) - && f.getType() == int.class) - .collect(Collectors.toList()); - - Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields()) - .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX) - && !DEPRECATED_VALUES.contains(f.getName()) - && Modifier.isStatic(f.getModifiers()) - && f.getType() == int.class) - .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName)); - - assertThat(enumsMap.size() - 1).isEqualTo(cujs.size()); - - cujs.forEach(f -> { - final int cuj = getIntFieldChecked(f); - final String cujName = f.getName(); - final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj) - ? ENUM_NAME_EXCEPTION_MAP.get(cuj) - : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4)); - final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj]; - final String enumName = enumsMap.get(enumKey); - final String expectedNameOfCuj = formatSimple("CUJ_%s", getNameOfCuj(cuj)); - - mExpect - .withMessage(formatSimple( - "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey)) - .that(expectedEnumName.equals(enumName)) - .isTrue(); - mExpect - .withMessage( - formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s", - cuj, cujName, expectedNameOfCuj)) - .that(cujName.equals(expectedNameOfCuj)) - .isTrue(); - }); - } - - @Test - public void testCujNameLimit() { - Arrays.stream(InteractionJankMonitor.class.getDeclaredFields()) - .filter(f -> f.getName().startsWith("CUJ_") - && Modifier.isStatic(f.getModifiers()) - && f.getType() == int.class) - .forEach(f -> { - final int cuj = getIntFieldChecked(f); - mExpect - .withMessage(formatSimple( - "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj))) - .that(getNameOfCuj(cuj).length()) - .isAtMost(MAX_LENGTH_OF_CUJ_NAME); - }); - } - - @Test public void testSessionNameLengthLimit() { - final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; - final String cujName = getNameOfCuj(cujType); + final int cujType = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; + final String cujName = Cuj.getNameOfCuj(cujType); final String cujTag = "ThisIsTheCujTag"; final String tooLongTag = cujTag.repeat(10); // Normal case, no postfix. - Session noPostfix = new Session(cujType, ""); - assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName)); + assertThat(generateSessionName(cujName, "")).isEqualTo(formatSimple("J<%s>", cujName)); // Normal case, with postfix. - Session withPostfix = new Session(cujType, cujTag); - assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag)); + assertThat(generateSessionName(cujName, cujTag)) + .isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag)); // Since the length of the cuj name is tested in another test, no need to test it here. // Too long postfix case, should trim the postfix and keep the cuj name completed. final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName, "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThi..."); - Session longPostfix = new Session(cujType, tooLongTag); - assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName); + assertThat(generateSessionName(cujName, tooLongTag)).isEqualTo(expectedTrimmedName); } private InteractionJankMonitor createMockedInteractionJankMonitor() { InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker)); - doReturn(true).when(monitor).shouldMonitor(anyInt()); + doReturn(true).when(monitor).shouldMonitor(); return monitor; } - private FrameTracker createMockedFrameTracker(InteractionJankMonitor monitor, - FrameTracker.FrameTrackerListener listener) { - Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX)); - doReturn(false).when(session).logToStatsd(); - + private FrameTracker createMockedFrameTracker() { ThreadedRendererWrapper threadedRenderer = mock(ThreadedRendererWrapper.class); doNothing().when(threadedRenderer).addObserver(any()); doNothing().when(threadedRenderer).removeObserver(any()); @@ -342,27 +192,19 @@ public class InteractionJankMonitorTest { Configuration configuration = mock(Configuration.class); when(configuration.isSurfaceOnly()).thenReturn(false); when(configuration.getView()).thenReturn(mView); - when(configuration.getHandler()).thenReturn(mView.getHandler()); when(configuration.getDisplayId()).thenReturn(42); + when(configuration.logToStatsd()).thenReturn(false); + when(configuration.getHandler()).thenReturn(mHandler); - FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(), + FrameTracker tracker = spy(new FrameTracker(configuration, threadedRenderer, viewRoot, surfaceControl, choreographer, new FrameMetricsWrapper(), new StatsLogWrapper(null), /* traceThresholdMissedFrames= */ 1, - /* traceThresholdFrameTimeMillis= */ -1, listener, configuration)); + /* traceThresholdFrameTimeMillis= */ -1, + /* listener */ null)); doNothing().when(tracker).postTraceStartMarker(any()); - doNothing().when(tracker).triggerPerfetto(); - doReturn(configuration.getHandler()).when(tracker).getHandler(); return tracker; } - - private int getIntFieldChecked(Field field) { - try { - return field.getInt(null); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } } diff --git a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java index fdba811f3eaa..fdde36ad8e35 100644 --- a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java @@ -30,8 +30,8 @@ import static com.android.internal.util.BitUtils.unpackBits; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java index 5f5bf1165004..e6ebfefd2aea 100644 --- a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -32,23 +34,21 @@ import android.text.style.TextAppearanceSpan; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; -import junit.framework.TestCase; - -import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; -public class ContrastColorUtilTest extends TestCase { - - private Context mContext; - - @Before - public void setUp() { - mContext = InstrumentationRegistry.getContext(); - } +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = Color.class) +public class ContrastColorUtilTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test @SmallTest public void testEnsureTextContrastAgainstDark() { int darkBg = 0xFF35302A; @@ -70,6 +70,7 @@ public class ContrastColorUtilTest extends TestCase { assertContrastIsWithinRange(selfContrastColor, darkBg, 4.5, 4.75); } + @Test @SmallTest public void testEnsureTextContrastAgainstLight() { int lightBg = 0xFFFFF8F2; @@ -91,13 +92,16 @@ public class ContrastColorUtilTest extends TestCase { assertContrastIsWithinRange(selfContrastColor, lightBg, 4.5, 4.75); } + @Test public void testBuilder_ensureColorSpanContrast_removesAllFullLengthColorSpans() { + Context context = InstrumentationRegistry.getContext(); + Spannable text = new SpannableString("blue text with yellow and green"); text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); text.setSpan(new ForegroundColorSpan(Color.BLUE), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - TextAppearanceSpan taSpan = new TextAppearanceSpan(mContext, + TextAppearanceSpan taSpan = new TextAppearanceSpan(context, R.style.TextAppearance_DeviceDefault_Notification_Title); assertThat(taSpan.getTextColor()).isNotNull(); // it must be set to prove it is cleared. text.setSpan(taSpan, 0, text.length(), @@ -123,6 +127,7 @@ public class ContrastColorUtilTest extends TestCase { assertThat(((ForegroundColorSpan) spans[2]).getForegroundColor()).isEqualTo(Color.GREEN); } + @Test public void testBuilder_ensureColorSpanContrast_partialLength_adjusted() { int background = 0xFFFF0101; // Slightly lighter red CharSequence text = new SpannableStringBuilder() @@ -138,14 +143,17 @@ public class ContrastColorUtilTest extends TestCase { assertContrastIsWithinRange(foregroundColor, background, 3, 3.2); } + @Test public void testBuilder_ensureColorSpanContrast_worksWithComplexInput() { + Context context = InstrumentationRegistry.getContext(); + Spannable text = new SpannableString("blue text with yellow and green and cyan"); text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); text.setSpan(new ForegroundColorSpan(Color.BLUE), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); // cyan TextAppearanceSpan - TextAppearanceSpan taSpan = new TextAppearanceSpan(mContext, + TextAppearanceSpan taSpan = new TextAppearanceSpan(context, R.style.TextAppearance_DeviceDefault_Notification_Title); taSpan = new TextAppearanceSpan(taSpan.getFamily(), taSpan.getTextStyle(), taSpan.getTextSize(), ColorStateList.valueOf(Color.CYAN), null); diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java index 36c2a62ae6ed..d2d3c134f390 100644 --- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java @@ -25,10 +25,16 @@ import static com.android.internal.util.DumpUtils.isPlatformPackage; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.content.ComponentName; import android.util.SparseArray; -import junit.framework.TestCase; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; @@ -37,7 +43,8 @@ import java.io.StringWriter; * Run with: atest FrameworksCoreTests:DumpUtilsTest */ -public class DumpUtilsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class DumpUtilsTest { private final StringWriter mStringWriter = new StringWriter(); private final PrintWriter mPrintWriter = new PrintWriter(mStringWriter); @@ -56,6 +63,7 @@ public class DumpUtilsTest extends TestCase { return () -> cn(componentName); } + @Test public void testIsPlatformPackage() { assertTrue(isPlatformPackage("android")); assertTrue(isPlatformPackage("android.abc")); @@ -79,6 +87,7 @@ public class DumpUtilsTest extends TestCase { assertFalse(isPlatformPackage(wcn("com.google.def/abc"))); } + @Test public void testIsNonPlatformPackage() { assertFalse(isNonPlatformPackage("android")); assertFalse(isNonPlatformPackage("android.abc")); @@ -102,6 +111,7 @@ public class DumpUtilsTest extends TestCase { assertTrue(isNonPlatformPackage(wcn("com.google.def/abc"))); } + @Test public void testIsPlatformCriticalPackage() { for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) { assertTrue(isPlatformCriticalPackage(() -> componentName)); @@ -115,6 +125,7 @@ public class DumpUtilsTest extends TestCase { assertFalse(isPlatformCriticalPackage(null)); } + @Test public void testIsPlatformNonCriticalPackage() { for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) { assertFalse(isPlatformNonCriticalPackage(() -> componentName)); @@ -128,6 +139,7 @@ public class DumpUtilsTest extends TestCase { assertFalse(isPlatformNonCriticalPackage(null)); } + @Test public void testFilterRecord() { assertFalse(filterRecord(null).test(wcn("com.google.p/abc"))); assertFalse(filterRecord(null).test(wcn("com.android.p/abc"))); @@ -178,6 +190,7 @@ public class DumpUtilsTest extends TestCase { wcn("com.google/.abc"))); } + @Test public void testDumpSparseArray_empty() { SparseArray<String> array = new SparseArray<>(); @@ -188,6 +201,7 @@ public class DumpUtilsTest extends TestCase { assertWithMessage("empty array dump").that(output).isEqualTo("...No whatevers\n"); } + @Test public void testDumpSparseArray_oneElement() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -201,6 +215,7 @@ public class DumpUtilsTest extends TestCase { + "..0: 1->uno\n"); } + @Test public void testDumpSparseArray_oneNullElement() { SparseArray<String> array = new SparseArray<>(); array.put(1, null); @@ -214,6 +229,7 @@ public class DumpUtilsTest extends TestCase { + "..0: 1->(null)\n"); } + @Test public void testDumpSparseArray_multipleElements() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -231,6 +247,7 @@ public class DumpUtilsTest extends TestCase { + "..2: 42->(null)\n"); } + @Test public void testDumpSparseArray_keyDumperOnly() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -251,6 +268,7 @@ public class DumpUtilsTest extends TestCase { + "_2=42_(null)\n"); } + @Test public void testDumpSparseArray_valueDumperOnly() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -272,6 +290,7 @@ public class DumpUtilsTest extends TestCase { + "..2: 42->(null)\n"); } + @Test public void testDumpSparseArray_keyAndValueDumpers() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -295,6 +314,7 @@ public class DumpUtilsTest extends TestCase { + "_2=42_(null)\n"); } + @Test public void testDumpSparseArrayValues() { SparseArray<String> array = new SparseArray<>(); array.put(1, "uno"); @@ -306,7 +326,7 @@ public class DumpUtilsTest extends TestCase { String output = flushPrintWriter(); assertWithMessage("dump of %s", array).that(output).isEqualTo("" - + ".3 numbers:\n" + + ".3 number(s):\n" + "..uno\n" + "..duo\n" + "..(null)\n"); diff --git a/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java b/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java index 589e4f98a32a..61d4e3da5036 100644 --- a/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java +++ b/core/tests/coretests/src/com/android/internal/util/DumpableContainerImplTest.java @@ -17,30 +17,32 @@ package com.android.internal.util; import static com.google.common.truth.Truth.assertWithMessage; -import static org.testng.Assert.assertThrows; - import android.util.Dumpable; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.dump.DumpableContainerImpl; import org.junit.Test; +import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; import java.util.concurrent.atomic.AtomicReference; -public final class DumpableContainerImplTest { +@RunWith(AndroidJUnit4.class) +public class DumpableContainerImplTest { private final DumpableContainerImpl mImpl = new DumpableContainerImpl(); private final StringWriter mSw = new StringWriter(); private final PrintWriter mWriter = new PrintWriter(mSw); - @Test + @Test(expected = NullPointerException.class) public void testAddDumpable_null() { - assertThrows(NullPointerException.class, () -> mImpl.addDumpable(null)); + mImpl.addDumpable(null); } - @Test + @Test(expected = NullPointerException.class) public void testAddDumpable_dumpableWithoutName() { Dumpable namelessDumpable = new Dumpable() { @@ -55,7 +57,7 @@ public final class DumpableContainerImplTest { } }; - assertThrows(NullPointerException.class, () -> mImpl.addDumpable(namelessDumpable)); + mImpl.addDumpable(namelessDumpable); } @Test @@ -179,9 +181,9 @@ public final class DumpableContainerImplTest { + "......6 Args: 4,8,15,16,23,42,\n"); } - @Test + @Test(expected = NullPointerException.class) public void testRemoveDumpable_null() { - assertThrows(NullPointerException.class, () -> mImpl.removeDumpable(null)); + mImpl.removeDumpable(null); } @Test diff --git a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java index 3946cdf76821..6bd67ea486d1 100644 --- a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java @@ -24,12 +24,15 @@ import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERAC import static com.google.common.truth.Truth.assertThat; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.DeviceConfig; -import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,7 +45,10 @@ import java.util.List; * {@link LatencyTrackerTest} */ @RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = DeviceConfig.class) public class FakeLatencyTrackerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private FakeLatencyTracker mFakeLatencyTracker; private int mInitialSyncDisabledMode; diff --git a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java index de325ab7d186..316b95ac1b7d 100644 --- a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java +++ b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.annotation.NonNull; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.ExceptionUtils; import com.android.modules.utils.FastDataInput; @@ -29,6 +30,7 @@ import com.android.modules.utils.FastDataOutput; import libcore.util.HexEncoding; import org.junit.Assume; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -51,6 +53,9 @@ import java.util.function.Consumer; @RunWith(Parameterized.class) public class FastDataTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private final boolean use4ByteSequence; private static final String TEST_SHORT_STRING = "a"; @@ -59,7 +64,12 @@ public class FastDataTest { @Parameters(name = "use4ByteSequence={0}") public static Collection<Object[]> data() { - return Arrays.asList(new Object[][] { {true}, {false} }); + if (RavenwoodRule.isUnderRavenwood()) { + // TODO: 4-byte sequences are only supported on ART + return Arrays.asList(new Object[][]{{false}}); + } else { + return Arrays.asList(new Object[][]{{true}, {false}}); + } } public FastDataTest(boolean use4ByteSequence) { diff --git a/core/tests/coretests/src/com/android/internal/util/FastMathTest.java b/core/tests/coretests/src/com/android/internal/util/FastMathTest.java new file mode 100644 index 000000000000..dd263345022b --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/FastMathTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import static org.junit.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class FastMathTest { + @Test + public void testRound() { + assertEquals(-1, FastMath.round(-1.0f)); + assertEquals(0, FastMath.round(0.0f)); + assertEquals(1, FastMath.round(1.0f)); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java new file mode 100644 index 000000000000..8456161f9709 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/GrowingArrayUtilsTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import static com.android.internal.util.GrowingArrayUtils.append; +import static com.android.internal.util.GrowingArrayUtils.insert; + +import static org.junit.Assert.assertArrayEquals; + +import android.util.EmptyArray; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class GrowingArrayUtilsTest { + private final Object TEST_OBJECT = new Object(); + + @Test + public void testAppend_Object() { + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT}, + append(EmptyArray.OBJECT, 0, TEST_OBJECT)); + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT, TEST_OBJECT}, + append(new Object[]{TEST_OBJECT}, 1, TEST_OBJECT)); + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT}, + append(new Object[]{null, null}, 0, TEST_OBJECT)); + } + + @Test + public void testInsert_Object() { + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT}, + insert(EmptyArray.OBJECT, 0, 0, TEST_OBJECT)); + assertArrayEqualsPrefix(new Object[]{null, TEST_OBJECT}, + insert(new Object[]{TEST_OBJECT}, 1, 0, null)); + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT, null}, + insert(new Object[]{TEST_OBJECT}, 1, 1, null)); + assertArrayEqualsPrefix(new Object[]{TEST_OBJECT, null, TEST_OBJECT}, + insert(new Object[]{TEST_OBJECT, TEST_OBJECT}, 2, 1, null)); + } + + @Test + public void testAppend_Int() { + assertArrayEqualsPrefix(new int[]{42}, + append(EmptyArray.INT, 0, 42)); + assertArrayEqualsPrefix(new int[]{42, 42}, + append(new int[]{42}, 1, 42)); + assertArrayEqualsPrefix(new int[]{42}, + append(new int[]{0, 0}, 0, 42)); + } + + @Test + public void testInsert_Int() { + assertArrayEqualsPrefix(new int[]{42}, + insert(EmptyArray.INT, 0, 0, 42)); + assertArrayEqualsPrefix(new int[]{21, 42}, + insert(new int[]{42}, 1, 0, 21)); + assertArrayEqualsPrefix(new int[]{42, 21}, + insert(new int[]{42}, 1, 1, 21)); + assertArrayEqualsPrefix(new int[]{42, 21, 43}, + insert(new int[]{42, 43}, 2, 1, 21)); + } + + @Test + public void testAppend_Long() { + assertArrayEqualsPrefix(new long[]{42}, + append(EmptyArray.LONG, 0, 42)); + assertArrayEqualsPrefix(new long[]{42, 42}, + append(new long[]{42}, 1, 42)); + assertArrayEqualsPrefix(new long[]{42}, + append(new long[]{0, 0}, 0, 42)); + } + + @Test + public void testInsert_Long() { + assertArrayEqualsPrefix(new long[]{42}, + insert(EmptyArray.LONG, 0, 0, 42)); + assertArrayEqualsPrefix(new long[]{21, 42}, + insert(new long[]{42}, 1, 0, 21)); + assertArrayEqualsPrefix(new long[]{42, 21}, + insert(new long[]{42}, 1, 1, 21)); + assertArrayEqualsPrefix(new long[]{42, 21, 43}, + insert(new long[]{42, 43}, 2, 1, 21)); + } + + @Test + public void testAppend_Boolean() { + assertArrayEqualsPrefix(new boolean[]{true}, + append(EmptyArray.BOOLEAN, 0, true)); + assertArrayEqualsPrefix(new boolean[]{true, true}, + append(new boolean[]{true}, 1, true)); + assertArrayEqualsPrefix(new boolean[]{true}, + append(new boolean[]{false, false}, 0, true)); + } + + @Test + public void testInsert_Boolean() { + assertArrayEqualsPrefix(new boolean[]{true}, + insert(EmptyArray.BOOLEAN, 0, 0, true)); + assertArrayEqualsPrefix(new boolean[]{false, true}, + insert(new boolean[]{true}, 1, 0, false)); + assertArrayEqualsPrefix(new boolean[]{true, false}, + insert(new boolean[]{true}, 1, 1, false)); + assertArrayEqualsPrefix(new boolean[]{true, false, true}, + insert(new boolean[]{true, true}, 2, 1, false)); + } + + private <T> void assertArrayEqualsPrefix(T[] expected, T[] actual) { + assertArrayEquals(expected, Arrays.copyOf(actual, expected.length)); + } + + private void assertArrayEqualsPrefix(int[] expected, int[] actual) { + assertArrayEquals(expected, Arrays.copyOf(actual, expected.length)); + } + + private void assertArrayEqualsPrefix(long[] expected, long[] actual) { + assertArrayEquals(expected, Arrays.copyOf(actual, expected.length)); + } + + private void assertArrayEqualsPrefix(boolean[] expected, boolean[] actual) { + assertArrayEquals(expected, Arrays.copyOf(actual, expected.length)); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java index f1cd89bf49f4..dcffa1cf6975 100644 --- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java +++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java @@ -19,14 +19,22 @@ package com.android.internal.util; import static com.android.internal.util.HexDump.hexStringToByteArray; import static com.android.internal.util.HexDump.toHexString; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Random; -public final class HexDumpTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public final class HexDumpTest { + @Test public void testBytesToHexString() { assertEquals("abcdef", HexDump.toHexString( new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, false)); @@ -34,12 +42,14 @@ public final class HexDumpTest extends TestCase { new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true)); } + @Test public void testNullByteArray() { assertThrows( NullPointerException.class, () -> HexDump.toHexString(null)); } + @Test public void testBytesToHexString_allByteValues() { byte[] bytes = new byte[256]; for (int i = 0; i < bytes.length; i++) { @@ -57,6 +67,7 @@ public final class HexDumpTest extends TestCase { assertEquals(expected, HexDump.toHexString(bytes)); } + @Test public void testRoundTrip_fromBytes() { Random deterministicRandom = new Random(31337); // arbitrary but deterministic for (int length = 0; length < 100; length++) { @@ -68,6 +79,7 @@ public final class HexDumpTest extends TestCase { } } + @Test public void testRoundTrip_fromString() { String hexString = "0123456789ABCDEF72f9a3438934c378d34f32a8b932"; for (int length = 0; length < hexString.length(); length += 2) { @@ -77,6 +89,7 @@ public final class HexDumpTest extends TestCase { } } + @Test public void testToHexString_offsetLength() { byte[] bytes = new byte[32]; for (int i = 0; i < 16; i++) { @@ -97,6 +110,7 @@ public final class HexDumpTest extends TestCase { } } + @Test public void testToHexString_case() { byte[] bytes = new byte[32]; for (int i = 0; i < 16; i++) { @@ -113,16 +127,19 @@ public final class HexDumpTest extends TestCase { assertEquals(expected.toUpperCase(), toHexString(bytes)); } + @Test public void testHexStringToByteArray_empty() { assertBytesEqual(new byte[0], HexDump.hexStringToByteArray("")); } + @Test public void testHexStringToByteArray_null() { assertThrows( NullPointerException.class, () -> HexDump.hexStringToByteArray((String) null)); } + @Test public void testHexStringToByteArray_invalidCharacters() { // IllegalArgumentException would probably have been better than RuntimeException, but it // might be too late to change now. @@ -137,6 +154,7 @@ public final class HexDumpTest extends TestCase { () -> HexDump.hexStringToByteArray("abcdefgh")); } + @Test public void testHexStringToByteArray_oddLength() { // IllegalArgumentException would probably have been better than // StringIndexOutOfBoundsException, but it might be too late to change now. diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java index f24894e7f263..010f72466f3a 100644 --- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java @@ -25,9 +25,11 @@ import static com.android.internal.util.LatencyTracker.STATSD_ACTION; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.DeviceConfig; -import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.LatencyTracker.ActionProperties; @@ -49,7 +51,11 @@ import java.util.Map; import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = DeviceConfig.class) public class LatencyTrackerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__"; @Rule diff --git a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java index b2a2265d30ae..e6418fae7dca 100644 --- a/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java +++ b/core/tests/coretests/src/com/android/internal/util/LineBreakBufferedWriterTest.java @@ -16,7 +16,13 @@ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.Writer; @@ -26,18 +32,18 @@ import java.util.List; /** * Tests for {@link IndentingPrintWriter}. */ -public class LineBreakBufferedWriterTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class LineBreakBufferedWriterTest { private ByteArrayOutputStream mStream; private RecordingWriter mWriter; - @Override - protected void setUp() throws Exception { - super.setUp(); - + @Before + public void setUp() throws Exception { mWriter = new RecordingWriter(); } + @Test public void testLessThanBufferSize() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 1000); @@ -49,6 +55,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("Hello\nWorld\nTest\n"); } + @Test public void testMoreThanBufferSizeNoLineBreaks() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -71,6 +78,7 @@ public class LineBreakBufferedWriterTest extends TestCase { } } + @Test public void testMoreThanBufferSizeNoLineBreaksSingleString() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -92,6 +100,7 @@ public class LineBreakBufferedWriterTest extends TestCase { } } + @Test public void testMoreThanBufferSizeLineBreakBefore() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -104,6 +113,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("aaaaaaaaaa", "bbbbcccccccccc"); } + @Test public void testMoreThanBufferSizeLineBreakBeforeSingleString() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -115,6 +125,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("aaaaaaaaaa", "bbbbcccccccccc"); } + @Test public void testMoreThanBufferSizeLineBreakNew() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -127,6 +138,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("aaaaaaaaaabbbbbc\nd", "ddddddddd"); } + @Test public void testMoreThanBufferSizeLineBreakBeforeAndNew() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -139,6 +151,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("aaaaaaaaaa\nbbbbbc\nd", "ddddddddd"); } + @Test public void testMoreThanBufferSizeInt() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 15); @@ -151,6 +164,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("123456789098765", "4321"); } + @Test public void testMoreThanBufferSizeChar() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 15); @@ -165,6 +179,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("$$$$$$$$$$%%%%%", "%%%%%"); } + @Test public void testMoreThanBufferSizeLineBreakNewChars() { final LineBreakBufferedWriter lw = new LineBreakBufferedWriter(mWriter, 20); @@ -177,6 +192,7 @@ public class LineBreakBufferedWriterTest extends TestCase { assertOutput("aaaaaaaaaabbbbbc\nd", "ddddddddd"); } + @Test public void testMoreThenInitialCapacitySimpleWrites() { // This check is different from testMoreThanBufferSizeChar. The initial capacity is lower // than the maximum buffer size here. diff --git a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java index 867152e720dd..d24cbfef9f10 100644 --- a/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java @@ -16,13 +16,23 @@ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; /** * Run with: atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/ParseUtilsTest.java */ -public class ParseUtilsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class ParseUtilsTest { + private static final float DELTA_FLOAT = 0.0f; + private static final double DELTA_DOUBLE = 0.0d; + + @Test public void testParseInt() { assertEquals(1, ParseUtils.parseInt(null, 1)); assertEquals(1, ParseUtils.parseInt("", 1)); @@ -33,6 +43,7 @@ public class ParseUtilsTest extends TestCase { assertEquals(-2, ParseUtils.parseInt("-2", 1)); } + @Test public void testParseIntWithBase() { assertEquals(1, ParseUtils.parseIntWithBase(null, 10, 1)); assertEquals(1, ParseUtils.parseIntWithBase("", 10, 1)); @@ -45,6 +56,7 @@ public class ParseUtilsTest extends TestCase { assertEquals(-3, ParseUtils.parseIntWithBase("-10", 3, 1)); } + @Test public void testParseLong() { assertEquals(1L, ParseUtils.parseLong(null, 1)); assertEquals(1L, ParseUtils.parseLong("", 1)); @@ -52,6 +64,7 @@ public class ParseUtilsTest extends TestCase { assertEquals(2L, ParseUtils.parseLong("2", 1)); } + @Test public void testParseLongWithBase() { assertEquals(1L, ParseUtils.parseLongWithBase(null, 10, 1)); assertEquals(1L, ParseUtils.parseLongWithBase("", 10, 1)); @@ -69,20 +82,23 @@ public class ParseUtilsTest extends TestCase { assertEquals(10_000_000_000L, ParseUtils.parseLongWithBase(null, 10, 10_000_000_000L)); } + @Test public void testParseFloat() { - assertEquals(0.5f, ParseUtils.parseFloat(null, 0.5f)); - assertEquals(0.5f, ParseUtils.parseFloat("", 0.5f)); - assertEquals(0.5f, ParseUtils.parseFloat("1x", 0.5f)); - assertEquals(1.5f, ParseUtils.parseFloat("1.5", 0.5f)); + assertEquals(0.5f, ParseUtils.parseFloat(null, 0.5f), DELTA_FLOAT); + assertEquals(0.5f, ParseUtils.parseFloat("", 0.5f), DELTA_FLOAT); + assertEquals(0.5f, ParseUtils.parseFloat("1x", 0.5f), DELTA_FLOAT); + assertEquals(1.5f, ParseUtils.parseFloat("1.5", 0.5f), DELTA_FLOAT); } + @Test public void testParseDouble() { - assertEquals(0.5, ParseUtils.parseDouble(null, 0.5)); - assertEquals(0.5, ParseUtils.parseDouble("", 0.5)); - assertEquals(0.5, ParseUtils.parseDouble("1x", 0.5)); - assertEquals(1.5, ParseUtils.parseDouble("1.5", 0.5)); + assertEquals(0.5, ParseUtils.parseDouble(null, 0.5), DELTA_DOUBLE); + assertEquals(0.5, ParseUtils.parseDouble("", 0.5), DELTA_DOUBLE); + assertEquals(0.5, ParseUtils.parseDouble("1x", 0.5), DELTA_DOUBLE); + assertEquals(1.5, ParseUtils.parseDouble("1.5", 0.5), DELTA_DOUBLE); } + @Test public void testParseBoolean() { assertEquals(false, ParseUtils.parseBoolean(null, false)); assertEquals(true, ParseUtils.parseBoolean(null, true)); diff --git a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java index 87f2a8a67947..0d213357c9a5 100644 --- a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java @@ -16,14 +16,28 @@ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = ProgressReporter.class) +public class ProgressReporterTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); -public class ProgressReporterTest extends TestCase { private ProgressReporter r; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { r = new ProgressReporter(0); } @@ -37,6 +51,7 @@ public class ProgressReporterTest extends TestCase { assertEquals("len", len, range[1]); } + @Test public void testBasic() throws Exception { assertProgress(0); @@ -50,6 +65,7 @@ public class ProgressReporterTest extends TestCase { assertProgress(100); } + @Test public void testSegment() throws Exception { r.setProgress(20); assertProgress(20); @@ -68,6 +84,7 @@ public class ProgressReporterTest extends TestCase { assertProgress(80); } + @Test public void testSegmentOvershoot() throws Exception { r.setProgress(20); assertProgress(20); @@ -87,6 +104,7 @@ public class ProgressReporterTest extends TestCase { assertProgress(60); } + @Test public void testSegmentNested() throws Exception { r.setProgress(20); assertProgress(20); diff --git a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java index 4497770ef40d..d7a100a30ac8 100644 --- a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java +++ b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java @@ -20,8 +20,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java index 8b30828a8936..ef579fe07af5 100644 --- a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java +++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java @@ -16,22 +16,29 @@ package com.android.internal.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import android.os.SystemClock; import android.text.format.DateUtils; -import junit.framework.TestCase; +import androidx.test.runner.AndroidJUnit4; -public class TokenBucketTest extends TestCase { +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class TokenBucketTest { static final int FILL_DELTA_VERY_SHORT = 1; static final int FILL_DELTA_VERY_LONG = Integer.MAX_VALUE; + @Test public void testArgumentValidation() { assertThrow(() -> new TokenBucket(0, 1, 1)); assertThrow(() -> new TokenBucket(1, 0, 1)); - assertThrow(() -> new TokenBucket(1, 1, 0)); assertThrow(() -> new TokenBucket(0, 1)); assertThrow(() -> new TokenBucket(1, 0)); assertThrow(() -> new TokenBucket(-1, 1, 1)); @@ -46,6 +53,7 @@ public class TokenBucketTest extends TestCase { new TokenBucket(5000, 1); } + @Test public void testInitialCapacity() { drain(new TokenBucket(FILL_DELTA_VERY_LONG, 1), 1); drain(new TokenBucket(FILL_DELTA_VERY_LONG, 10), 10); @@ -62,6 +70,7 @@ public class TokenBucketTest extends TestCase { drain(new TokenBucket((int) DateUtils.DAY_IN_MILLIS, 200), 200); } + @Test public void testReset() { TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_LONG, 100, 10); drain(tb, 10); @@ -77,6 +86,7 @@ public class TokenBucketTest extends TestCase { drain(tb, 30); } + @Test public void testFill() throws Exception { int delta = 50; TokenBucket tb = new TokenBucket(delta, 10, 0); @@ -88,6 +98,7 @@ public class TokenBucketTest extends TestCase { assertTrue(tb.has()); } + @Test public void testRefill() throws Exception { TokenBucket tb = new TokenBucket(FILL_DELTA_VERY_SHORT, 10, 10); @@ -107,6 +118,7 @@ public class TokenBucketTest extends TestCase { assertEquals(10, tb.get(100)); } + @Test public void testAverage() throws Exception { final int delta = 3; final int want = 60; @@ -124,6 +136,7 @@ public class TokenBucketTest extends TestCase { assertDuration(want * delta, SystemClock.elapsedRealtime() - start); } + @Test public void testBurst() throws Exception { final int delta = 2; final int capacity = 20; diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java index 8e653f5e828f..9cb91229ce58 100644 --- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.SystemClock; import android.os.UserHandle; import androidx.test.runner.AndroidJUnit4; @@ -38,6 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -185,16 +187,44 @@ public class PackageMonitorTest { Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED); intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID); intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID); + final long elapsedRealtimeMs = SystemClock.elapsedRealtime(); + intent.putExtra(Intent.EXTRA_TIME, elapsedRealtimeMs); intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null)); spyPackageMonitor.doHandlePackageEvent(intent); verify(spyPackageMonitor, times(1)).onBeginPackageChanges(); verify(spyPackageMonitor, times(1)).onHandleForceStop(eq(intent), - eq(new String[]{FAKE_PACKAGE_NAME}), eq(FAKE_PACKAGE_UID), eq(true)); + eq(new String[]{FAKE_PACKAGE_NAME}), eq(FAKE_PACKAGE_UID), eq(true), + eqTimestamp(elapsedRealtimeMs)); verify(spyPackageMonitor, times(1)).onFinishPackageChanges(); } @Test + public void testPackageMonitorDoHandlePackageEventPackageUnstopped() throws Exception { + PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor()); + + Intent intent = new Intent(Intent.ACTION_PACKAGE_UNSTOPPED); + intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID); + intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID); + final long elapsedRealtimeMs = SystemClock.elapsedRealtime(); + intent.putExtra(Intent.EXTRA_TIME, elapsedRealtimeMs); + intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null)); + spyPackageMonitor.doHandlePackageEvent(intent); + + verify(spyPackageMonitor, times(1)).onBeginPackageChanges(); + verify(spyPackageMonitor, times(1)).onPackageUnstopped( + eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eqTimestamp(elapsedRealtimeMs)); + verify(spyPackageMonitor, times(1)).onFinishPackageChanges(); + } + + private static Bundle eqTimestamp(long expectedRealtimeMs) { + return ArgumentMatchers.argThat(actualExtras -> { + final long actualRealtimeMs = actualExtras.getLong(Intent.EXTRA_TIME); + return expectedRealtimeMs == actualRealtimeMs; + }); + } + + @Test public void testPackageMonitorDoHandlePackageEventPackageQueryRestarted() throws Exception { PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor()); diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp index 967047e74555..2ccee71c8ff7 100644 --- a/core/tests/utiltests/Android.bp +++ b/core/tests/utiltests/Android.bp @@ -54,21 +54,20 @@ android_test { android_ravenwood_test { name: "FrameworksUtilTestsRavenwood", + libs: [ + "android.test.mock", + ], static_libs: [ "androidx.annotation_annotation", "androidx.test.rules", "mockito_ravenwood", + "frameworks-base-testutils", + "servicestests-utils", ], srcs: [ - "src/android/util/AtomicFileTest.java", - "src/android/util/DataUnitTest.java", - "src/android/util/EventLogTest.java", - "src/android/util/IndentingPrintWriterTest.java", - "src/android/util/IntArrayTest.java", - "src/android/util/LocalLogTest.java", - "src/android/util/LongArrayTest.java", - "src/android/util/SlogTest.java", - "src/android/util/TimeUtilsTest.java", + "src/android/util/IRemoteMemoryIntArray.aidl", + "src/android/util/**/*.java", + "src/com/android/internal/util/**/*.java", ], auto_gen_config: true, } diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java index 1966e122ee5b..51013e4b4f00 100644 --- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java +++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java @@ -23,11 +23,14 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Parcel; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.runner.AndroidJUnit4; import libcore.io.IoUtils; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,13 +38,17 @@ import java.lang.reflect.Field; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - -import androidx.test.ext.junit.runners.AndroidJUnit4; @RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = MemoryIntArray.class) public class MemoryIntArrayTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + static { - System.loadLibrary("cutils"); - System.loadLibrary("memoryintarraytest"); + if (!RavenwoodRule.isUnderRavenwood()) { + System.loadLibrary("cutils"); + System.loadLibrary("memoryintarraytest"); + } } @Test diff --git a/core/tests/utiltests/src/android/util/MetadataReaderTest.java b/core/tests/utiltests/src/android/util/MetadataReaderTest.java index a828edbac6ab..14feed8b89a1 100644 --- a/core/tests/utiltests/src/android/util/MetadataReaderTest.java +++ b/core/tests/utiltests/src/android/util/MetadataReaderTest.java @@ -16,37 +16,47 @@ package android.util; +import static org.junit.Assert.assertEquals; + import android.media.ExifInterface; import android.os.Bundle; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.DocumentsContract; import android.provider.MetadataReader; -import libcore.io.IoUtils; +import androidx.test.runner.AndroidJUnit4; -import junit.framework.TestCase; +import libcore.io.IoUtils; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -public class MetadataReaderTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = MetadataReader.class) +public class MetadataReaderTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private InputStream mInputStream; private Bundle mData; @Before - protected void setUp() throws Exception { + public void setUp() throws Exception { mInputStream = getClass().getClassLoader().getResourceAsStream("res/drawable/image.jpg"); mData = new Bundle(); } @After - protected void tearDown() throws Exception { + public void tearDown() throws Exception { IoUtils.closeQuietly(mInputStream); } diff --git a/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java b/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java index 5f6c20141c37..3bb79ec91d09 100644 --- a/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java +++ b/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java @@ -21,12 +21,22 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import android.os.SystemClock; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mockito; - +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class) public class SystemConfigFileCommitEventLoggerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test public void testSimple() throws Exception { var logger = spy(new SystemConfigFileCommitEventLogger("name")); diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java index 72f3af640b67..0c5e9664bbde 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java @@ -17,19 +17,28 @@ package com.android.internal.util; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import junit.framework.TestCase; - /** * Tests for {@link ArrayUtils} */ -public class ArrayUtilsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class ArrayUtilsTest { + @Test public void testContains() throws Exception { final Object A = new Object(); final Object B = new Object(); @@ -46,6 +55,7 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.contains(new Object[] { null }, A)); } + @Test public void testIndexOf() throws Exception { final Object A = new Object(); final Object B = new Object(); @@ -66,6 +76,7 @@ public class ArrayUtilsTest extends TestCase { assertEquals(2, ArrayUtils.indexOf(new Object[] { A, null, B }, B)); } + @Test public void testContainsAll() throws Exception { final Object A = new Object(); final Object B = new Object(); @@ -86,6 +97,7 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null })); } + @Test public void testContainsInt() throws Exception { assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 1)); assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 2)); @@ -96,6 +108,7 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.contains(new int[] { }, 2)); } + @Test public void testAppendInt() throws Exception { assertArrayEquals(new int[] { 1 }, ArrayUtils.appendInt(null, 1)); @@ -107,6 +120,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.appendInt(new int[] { 1, 2 }, 1)); } + @Test public void testRemoveInt() throws Exception { assertNull(ArrayUtils.removeInt(null, 1)); assertArrayEquals(new int[] { }, @@ -123,6 +137,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1)); } + @Test public void testContainsLong() throws Exception { assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 1)); assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 2)); @@ -133,6 +148,7 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.contains(new long[] { }, 2)); } + @Test public void testAppendLong() throws Exception { assertArrayEquals(new long[] { 1 }, ArrayUtils.appendLong(null, 1)); @@ -144,6 +160,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.appendLong(new long[] { 1, 2 }, 1)); } + @Test public void testRemoveLong() throws Exception { assertNull(ArrayUtils.removeLong(null, 1)); assertArrayEquals(new long[] { }, @@ -160,6 +177,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1)); } + @Test public void testConcat_zeroObjectArrays() { // empty varargs array assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class)); @@ -167,16 +185,19 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, (String[][]) null)); } + @Test public void testConcat_oneObjectArray() { assertArrayEquals(new String[] { "1", "2" }, ArrayUtils.concat(String.class, new String[] { "1", "2" })); } + @Test public void testConcat_oneEmptyObjectArray() { assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, (String[]) null)); assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, new String[] {})); } + @Test public void testConcat_twoObjectArrays() { assertArrayEquals(new Long[] { 1L }, ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {})); @@ -188,6 +209,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L })); } + @Test public void testConcat_twoEmptyObjectArrays() { assertArrayEquals(new Long[] {}, ArrayUtils.concat(Long.class, null, null)); assertArrayEquals(new Long[] {}, ArrayUtils.concat(Long.class, new Long[] {}, null)); @@ -196,6 +218,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {})); } + @Test public void testConcat_threeObjectArrays() { String[] array1 = { "1", "2" }; String[] array2 = { "3", "4" }; @@ -205,6 +228,7 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(expectation, ArrayUtils.concat(String.class, array1, array2, array3)); } + @Test public void testConcat_threeObjectArraysWithNull() { String[] array1 = { "1", "2" }; String[] array2 = null; @@ -214,6 +238,7 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(expectation, ArrayUtils.concat(String.class, array1, array2, array3)); } + @Test public void testConcat_zeroByteArrays() { // empty varargs array assertArrayEquals(new byte[] {}, ArrayUtils.concat()); @@ -221,15 +246,18 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[][]) null)); } + @Test public void testConcat_oneByteArray() { assertArrayEquals(new byte[] { 1, 2 }, ArrayUtils.concat(new byte[] { 1, 2 })); } + @Test public void testConcat_oneEmptyByteArray() { assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[]) null)); assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {})); } + @Test public void testConcat_twoByteArrays() { assertArrayEquals(new byte[] { 1 }, ArrayUtils.concat(new byte[] { 1 }, new byte[] {})); assertArrayEquals(new byte[] { 1 }, ArrayUtils.concat(new byte[] {}, new byte[] { 1 })); @@ -239,6 +267,7 @@ public class ArrayUtilsTest extends TestCase { ArrayUtils.concat(new byte[] { 1, 2 }, new byte[] { 3, 4 })); } + @Test public void testConcat_twoEmptyByteArrays() { assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[]) null, null)); assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {}, null)); @@ -246,6 +275,7 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {}, new byte[] {})); } + @Test public void testConcat_threeByteArrays() { byte[] array1 = { 1, 2 }; byte[] array2 = { 3, 4 }; @@ -255,6 +285,7 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3)); } + @Test public void testConcat_threeByteArraysWithNull() { byte[] array1 = { 1, 2 }; byte[] array2 = null; @@ -264,6 +295,7 @@ public class ArrayUtilsTest extends TestCase { assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3)); } + @Test @SmallTest public void testUnstableRemoveIf() throws Exception { java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() { @@ -357,31 +389,37 @@ public class ArrayUtilsTest extends TestCase { assertEquals(0, collection.size()); } + @Test @SmallTest public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() { ArrayUtils.throwsIfOutOfBounds(10, 2, 6); } + @Test @SmallTest public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() { ArrayUtils.throwsIfOutOfBounds(10, 0, 10); } + @Test @SmallTest public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() { ArrayUtils.throwsIfOutOfBounds(10, 0, 0); } + @Test @SmallTest public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() { ArrayUtils.throwsIfOutOfBounds(10, 10, 0); } + @Test @SmallTest public void testThrowsIfOutOfBounds_passesWhenEmptyArray() { ArrayUtils.throwsIfOutOfBounds(0, 0, 0); } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() { try { @@ -392,6 +430,7 @@ public class ArrayUtilsTest extends TestCase { } } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenCountNegative() { try { @@ -402,6 +441,7 @@ public class ArrayUtilsTest extends TestCase { } } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() { try { @@ -412,6 +452,7 @@ public class ArrayUtilsTest extends TestCase { } } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() { try { @@ -422,6 +463,7 @@ public class ArrayUtilsTest extends TestCase { } } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenLengthNegative() { try { @@ -432,6 +474,7 @@ public class ArrayUtilsTest extends TestCase { } } + @Test @SmallTest public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() { try { diff --git a/core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java b/core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java index 306f58fcce2e..092d6999e85e 100644 --- a/core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java @@ -16,20 +16,23 @@ package com.android.internal.util; -import com.android.internal.util.BitwiseInputStream; -import com.android.internal.util.BitwiseOutputStream; -import com.android.internal.util.HexDump; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; import android.util.Log; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Random; -public class BitwiseStreamsTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class BitwiseStreamsTest { private final static String LOG_TAG = "BitwiseStreamsTest"; + @Test @SmallTest public void testOne() throws Exception { int offset = 3; @@ -45,6 +48,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } + @Test @SmallTest public void testTwo() throws Exception { int offset = 3; @@ -59,6 +63,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } + @Test @SmallTest public void testThree() throws Exception { int offset = 4; @@ -73,6 +78,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } + @Test @SmallTest public void testFour() throws Exception { int offset = 7; @@ -90,6 +96,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup)); } + @Test @SmallTest public void testFive() throws Exception { Random random = new Random(); @@ -111,6 +118,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { } } + @Test @SmallTest public void testSix() throws Exception { int num_runs = 10; @@ -134,6 +142,7 @@ public class BitwiseStreamsTest extends AndroidTestCase { Log.d(LOG_TAG, "repeated encode-decode took " + (end - start) + " ms"); } + @Test @SmallTest public void testExpandArray() throws Exception { Random random = new Random(); diff --git a/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java index 1581abb5a9c6..9a1402e04011 100644 --- a/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java @@ -15,19 +15,21 @@ */ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Objects; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -public class CallbackRegistryTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class CallbackRegistryTest { final Integer callback1 = 1; final Integer callback2 = 2; @@ -50,6 +52,7 @@ public class CallbackRegistryTest extends TestCase { deepNotifyCount[callback]++; } + @Test public void testAddListener() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -89,6 +92,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(otherListener, callbacks.get(0)); } + @Test public void testSimpleNotify() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -108,6 +112,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(1, notify2); } + @Test public void testRemoveWhileNotifying() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -135,6 +140,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(callback3, callbacks.get(0)); } + @Test public void testDeepRemoveWhileNotifying() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -159,6 +165,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(0, callbacks.size()); } + @Test public void testAddRemovedListener() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = @@ -191,6 +198,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(1, notify3); } + @Test public void testVeryDeepRemoveWhileNotifying() { final Integer[] callbacks = new Integer[deepNotifyCount.length]; for (int i = 0; i < callbacks.length; i++) { @@ -221,6 +229,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(0, callbackList.size()); } + @Test public void testClear() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -245,6 +254,7 @@ public class CallbackRegistryTest extends TestCase { } } + @Test public void testNestedClear() { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -268,6 +278,7 @@ public class CallbackRegistryTest extends TestCase { assertEquals(0, callbackList.size()); } + @Test public void testIsEmpty() throws Exception { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { @@ -284,6 +295,7 @@ public class CallbackRegistryTest extends TestCase { assertFalse(registry.isEmpty()); } + @Test public void testClone() throws Exception { CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { diff --git a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java index 469a4ccd05e4..988854038e53 100644 --- a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java @@ -18,12 +18,26 @@ package com.android.internal.util; import static com.android.internal.util.CharSequences.forAsciiBytes; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -import junit.framework.TestCase; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; -public class CharSequencesTest extends TestCase { +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = CharSequences.class) +public class CharSequencesTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test @SmallTest public void testCharSequences() { String s = "Hello Bob"; diff --git a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java index 3cef33621a01..7723d589a723 100644 --- a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java @@ -16,13 +16,23 @@ package com.android.internal.util; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; +import android.provider.DeviceConfig; import android.util.Log; import android.util.Xml; -import junit.framework.TestCase; +import androidx.test.filters.LargeTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -34,7 +44,9 @@ import java.nio.charset.StandardCharsets; * Tests for {@link FastXmlSerializer} */ @SmallTest -public class FastXmlSerializerTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = Xml.class) +public class FastXmlSerializerTest { private static final String TAG = "FastXmlSerializerTest"; private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE. @@ -42,6 +54,10 @@ public class FastXmlSerializerTest extends TestCase { private static final String ROOT_TAG = "root"; private static final String ATTR = "attr"; + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test public void testEmptyText() throws Exception { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -128,6 +144,7 @@ public class FastXmlSerializerTest extends TestCase { return ok; } + @Test @LargeTest public void testAllCharacters() throws Exception { boolean ok = true; diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java index 73e47e1635b4..47c17959d28d 100644 --- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java @@ -23,16 +23,23 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.YEAR_IN_MILLIS; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.Suppress; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import android.util.Log; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.FileRotator.Reader; import com.android.internal.util.FileRotator.Writer; -import com.android.internal.util.test.FsUtil; import com.google.android.collect.Lists; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -40,6 +47,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; @@ -47,7 +55,8 @@ import java.util.Random; /** * Tests for {@link FileRotator}. */ -public class FileRotatorTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class FileRotatorTest { private static final String TAG = "FileRotatorTest"; private File mBasePath; @@ -59,14 +68,12 @@ public class FileRotatorTest extends AndroidTestCase { // TODO: test throwing rolls back correctly - @Override - protected void setUp() throws Exception { - super.setUp(); - - mBasePath = getContext().getFilesDir(); - FsUtil.deleteContents(mBasePath); + @Before + public void setUp() throws Exception { + mBasePath = Files.createTempDirectory(TAG).toFile(); } + @Test public void testEmpty() throws Exception { final FileRotator rotate1 = new FileRotator( mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS); @@ -85,6 +92,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate2); } + @Test public void testCombine() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS); @@ -106,6 +114,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "bar"); } + @Test public void testRotate() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, DAY_IN_MILLIS, WEEK_IN_MILLIS); @@ -138,6 +147,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "bar", "baz"); } + @Test public void testDelete() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, MINUTE_IN_MILLIS, DAY_IN_MILLIS); @@ -168,6 +178,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate); } + @Test public void testThrowRestoresBackup() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, MINUTE_IN_MILLIS, DAY_IN_MILLIS); @@ -201,6 +212,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "foo"); } + @Test public void testOtherFilesAndMalformed() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS); @@ -229,6 +241,7 @@ public class FileRotatorTest extends AndroidTestCase { private static final String BLUE = "blue"; private static final String YELLOW = "yellow"; + @Test public void testQueryMatch() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, HOUR_IN_MILLIS, YEAR_IN_MILLIS); @@ -277,6 +290,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadMatching(rotate, Long.MIN_VALUE, TEST_TIME - DAY_IN_MILLIS); } + @Test public void testClockRollingBackwards() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, DAY_IN_MILLIS, YEAR_IN_MILLIS); @@ -325,7 +339,8 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "meow", "yay"); } - @Suppress + @Test + @Ignore public void testFuzz() throws Exception { final FileRotator rotate = new FileRotator( mBasePath, PREFIX, HOUR_IN_MILLIS, DAY_IN_MILLIS); @@ -352,6 +367,7 @@ public class FileRotatorTest extends AndroidTestCase { Log.d(TAG, Arrays.toString(mBasePath.list())); } + @Test public void testRecoverAtomic() throws Exception { write("rotator.1024-2048", "foo"); write("rotator.1024-2048.backup", "bar"); @@ -366,6 +382,7 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "bar"); } + @Test public void testReadSorted() throws Exception { write("rotator.1024-2048", "2"); write("rotator.2048-4096", "3"); @@ -376,11 +393,11 @@ public class FileRotatorTest extends AndroidTestCase { assertReadAll(rotate, "1", "2", "3"); } + @Test public void testFileSystemInaccessible() throws Exception { - File inaccessibleDir = null; - String dirPath = getContext().getFilesDir() + File.separator + "inaccessible"; - inaccessibleDir = new File(dirPath); - final FileRotator rotate = new FileRotator(inaccessibleDir, PREFIX, SECOND_IN_MILLIS, SECOND_IN_MILLIS); + File inaccessibleDir = mBasePath.toPath().resolve("does_not_exist").toFile(); + final FileRotator rotate = new FileRotator(inaccessibleDir, PREFIX, + SECOND_IN_MILLIS, SECOND_IN_MILLIS); // rotate should not throw on dir not mkdir-ed (or otherwise inaccessible) rotate.maybeRotate(TEST_TIME); diff --git a/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java b/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java index f2285a12e30a..74184cacc91c 100644 --- a/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/HeavyHitterSketchTest.java @@ -16,10 +16,17 @@ package com.android.internal.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import android.util.ArraySet; import android.util.Pair; -import junit.framework.TestCase; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Comparator; @@ -32,7 +39,8 @@ import java.util.stream.Collectors; /** * Tests for {@link HeavyHitterSketch}. */ -public final class HeavyHitterSketchTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public final class HeavyHitterSketchTest { private static final float EPSILON = 0.00001f; @@ -163,6 +171,7 @@ public final class HeavyHitterSketchTest extends TestCase { return input; } + @Test public void testPositive() throws Exception { // Simple case verify(new int[]{2, 9, 9, 9, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{9}, @@ -179,6 +188,7 @@ public final class HeavyHitterSketchTest extends TestCase { new float[]{0.32f, 0.24f, 0.16f, 0.08f}); } + @Test public void testNegative() throws Exception { // Simple case verifyNotExpected(new int[]{2, 9, 9, 9, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{0, 1, 2}); @@ -193,6 +203,7 @@ public final class HeavyHitterSketchTest extends TestCase { verifyNotExpected(input, 12, new int[]{0, 1, 2, 1000, 1005}); } + @Test public void testFalsePositive() throws Exception { // Simple case verifyNotExpected(new int[]{2, 9, 2, 2, 7, 6, 4, 9, 9, 9, 3, 9}, 2, new int[]{9}); diff --git a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java index 35c56818a108..7203b8c6a8c8 100644 --- a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java @@ -22,20 +22,28 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.graphics.Color; import android.os.Binder; import android.os.Bundle; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.InlinePresentationStyleUtils; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest +@IgnoreUnderRavenwood(blockedBy = InlinePresentationStyleUtils.class) public class InlinePresentationStyleUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test public void testBundleEquals_empty() { Bundle bundle1 = new Bundle(); diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java index 0b7019995acb..0df5b0a4093e 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java @@ -43,6 +43,8 @@ import android.content.pm.UserInfo; import android.os.Looper; import android.os.RemoteException; import android.os.UserManager; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.Settings; import android.test.mock.MockContentResolver; @@ -58,6 +60,7 @@ import com.android.internal.widget.LockPatternUtils; import com.google.android.collect.Lists; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -68,7 +71,10 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest +@IgnoreUnderRavenwood(blockedBy = LockPatternUtils.class) public class LockPatternUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private static final int DEMO_USER_ID = 5; diff --git a/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java index 32b969a8d1b5..36f238e7429c 100644 --- a/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java @@ -19,10 +19,13 @@ package com.android.internal.util; import static org.junit.Assert.*; import com.android.internal.util.MessageUtils; -import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import android.util.SparseArray; import org.junit.Test; +import org.junit.runner.RunWith; class A { @@ -48,6 +51,7 @@ class B { * Unit tests for {@link com.android.util.MessageUtils}. */ @SmallTest +@RunWith(AndroidJUnit4.class) public class MessageUtilsTest { private static final Class[] CLASSES = { A.class, B.class }; diff --git a/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java index 4412c2c6949c..6c3479722526 100644 --- a/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java @@ -16,12 +16,27 @@ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Tests for {@link MimeIconUtils}. */ -public class MimeIconUtilsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = MimeIconUtils.class) +public class MimeIconUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + + @Test public void testSimple() throws Exception { assertEquals("PNG image", MimeIconUtils.getTypeInfo("image/png").getLabel()); diff --git a/core/tests/utiltests/src/com/android/internal/util/ObjectUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ObjectUtilsTest.java index 443183eeba4d..4eaacfffd433 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ObjectUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/ObjectUtilsTest.java @@ -15,11 +15,18 @@ */ package com.android.internal.util; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest -public class ObjectUtilsTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class ObjectUtilsTest { + @Test public void testCompare() { assertEquals(0, ObjectUtils.compare(null, null)); assertEquals(1, ObjectUtils.compare("a", null)); diff --git a/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java index d124ad9ddfb0..c852e9f17bc0 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java @@ -29,14 +29,19 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ObservableServiceConnection.ServiceTransformer; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -46,7 +51,12 @@ import java.util.Queue; import java.util.concurrent.Executor; @SmallTest +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = ObservableServiceConnection.class) public class ObservableServiceConnectionTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private static final ComponentName COMPONENT_NAME = new ComponentName("test.package", "component"); diff --git a/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java index fee46545ac62..096f303247ff 100644 --- a/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java @@ -30,6 +30,10 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ObservableServiceConnection.ServiceTransformer; import com.android.server.testutils.OffsettableClock; @@ -37,7 +41,9 @@ import com.android.server.testutils.TestHandler; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -45,7 +51,12 @@ import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.Executor; +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = PersistentServiceConnection.class) public class PersistentServiceConnectionTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private static final ComponentName COMPONENT_NAME = new ComponentName("test.package", "component"); private static final int MAX_RETRIES = 2; diff --git a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java index b93276053caf..4c00c1667e3c 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java @@ -16,7 +16,15 @@ package com.android.internal.util; -import android.test.AndroidTestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.File; @@ -28,8 +36,10 @@ import java.nio.file.Files; /** * Tests for {@link ProcFileReader}. */ -public class ProcFileReaderTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class ProcFileReaderTest { + @Test public void testEmpty() throws Exception { final ProcFileReader reader = buildReader(""); @@ -43,6 +53,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testSingleString() throws Exception { final ProcFileReader reader = buildReader("a\nb\nc\n"); @@ -59,6 +70,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testMixedNumbersSkip() throws Exception { final ProcFileReader reader = buildReader("1 2 3\n4 abc_def 5 6 7 8 9\n10\n"); @@ -79,6 +91,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testBufferSize() throws Exception { // read numbers using very small buffer size, exercising fillBuf() final ProcFileReader reader = buildReader("1 21 3 41 5 61 7 81 9 10\n", 3); @@ -97,6 +110,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testBlankLines() throws Exception { final ProcFileReader reader = buildReader("1\n\n2\n\n3\n"); @@ -117,6 +131,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testMinMax() throws Exception { final ProcFileReader reader = buildReader( "1 -1024 9223372036854775807 -9223372036854775808\n"); @@ -129,6 +144,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testDelimiterNeverFound() throws Exception { final ProcFileReader reader = buildReader("teststringwithoutdelimiters"); @@ -141,6 +157,7 @@ public class ProcFileReaderTest extends AndroidTestCase { } } + @Test public void testLargerThanBuffer() throws Exception { // try finishing line larger than buffer final ProcFileReader reader = buildReader("1 teststringlongerthanbuffer\n", 4); @@ -155,6 +172,7 @@ public class ProcFileReaderTest extends AndroidTestCase { } } + @Test public void testOptionalLongs() throws Exception { final ProcFileReader reader = buildReader("123 456\n789\n"); @@ -169,6 +187,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertEquals(-1L, reader.nextOptionalLong(-1L)); } + @Test public void testInvalidLongs() throws Exception { final ProcFileReader reader = buildReader("12: 34\n56 78@#\n"); @@ -183,6 +202,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testConsecutiveDelimiters() throws Exception { final ProcFileReader reader = buildReader("1 2 3 4 5\n"); @@ -195,6 +215,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testIgnore() throws Exception { final ProcFileReader reader = buildReader("a b c\n"); @@ -209,6 +230,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } + @Test public void testRewind() throws Exception { final ProcFileReader reader = buildReader("abc\n"); @@ -224,7 +246,7 @@ public class ProcFileReaderTest extends AndroidTestCase { assertFalse(reader.hasMoreData()); } - + @Test public void testRewindFileInputStream() throws Exception { File tempFile = File.createTempFile("procfile", null, null); Files.write(tempFile.toPath(), "abc\n".getBytes(StandardCharsets.US_ASCII)); diff --git a/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java index 1b9d2ef58c10..1eb5e3ab7d6c 100644 --- a/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java @@ -16,7 +16,12 @@ package com.android.internal.util; -import junit.framework.TestCase; +import static org.junit.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Arrays; import java.util.List; @@ -24,8 +29,10 @@ import java.util.List; /** * Tests for {@link QuickSelect}. */ -public final class QuickSelectTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class QuickSelectTest { + @Test public void testQuickSelect() throws Exception { test((List<Integer>) null, 0, null); test(Arrays.asList(), -1, null); diff --git a/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java index f67fd516fcf6..6523ed7fabb7 100644 --- a/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java @@ -16,12 +16,21 @@ package com.android.internal.util; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest -public final class StringPoolTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public final class StringPoolTest { + @Test public void testStringPool() { StringPool stringPool = new StringPool(); String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3); @@ -29,6 +38,7 @@ public final class StringPoolTest extends AndroidTestCase { assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3)); } + @Test public void testHashCollision() { StringPool stringPool = new StringPool(); char[] a = { (char) 1, (char) 0 }; diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java index 734ebef0007e..b0db8a1da3a8 100644 --- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java @@ -24,11 +24,17 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.test.suitebuilder.annotation.SmallTest; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -40,6 +46,8 @@ import org.mockito.stubbing.Answer; * Unit tests for {@link com.android.internal.util.WakeupMessage}. */ @SmallTest +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = WakeupMessage.class) public class WakeupMessageTest { private static final String TEST_CMD_NAME = "TEST cmd Name"; private static final int TEST_CMD = 18; @@ -47,11 +55,16 @@ public class WakeupMessageTest { private static final int TEST_ARG2 = 182; private static final Object TEST_OBJ = "hello"; + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + @Mock Context mContext; @Mock AlarmManager mAlarmManager; WakeupMessage mMessage; // Make a spy so that we can verify calls to it - @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler(); + @Spy MessageCapturingHandler mHandler; ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor = ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); @@ -85,6 +98,8 @@ public class WakeupMessageTest { */ @Before public void setUp() { + mHandler = new MessageCapturingHandler(); + MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); diff --git a/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java index 0484068bf900..24eb213751bc 100644 --- a/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java @@ -17,13 +17,17 @@ package com.android.internal.util; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import android.util.Xml; +import androidx.test.runner.AndroidJUnit4; + import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -32,9 +36,11 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; -public class XmlUtilsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class XmlUtilsTest { // https://code.google.com/p/android/issues/detail?id=63717 + @Test public void testMapWithNullKeys() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); Map<String, Object> map = new HashMap<String, Object>(); @@ -48,6 +54,7 @@ public class XmlUtilsTest extends TestCase { assertEquals("fooValue", deserialized.get("foo")); } + @Test public void testreadWriteXmlByteArrayValue() throws Exception { byte[] testByteArray = {0x1 , 0xa, 0xb, 0x9, 0x34, (byte) 0xaa, (byte) 0xba, (byte) 0x99}; diff --git a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java b/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java index f2be109c8602..502d6b6d7270 100644 --- a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java @@ -16,33 +16,41 @@ package com.android.internal.util.test; -import android.content.ContentResolver; -import android.database.ContentObserver; -import android.net.Uri; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.content.ContentProvider; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; import android.provider.Settings; -import android.test.AndroidTestCase; import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; -import android.util.Log; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; /** * Unit tests for FakeSettingsProvider. */ -public class FakeSettingsProviderTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +@IgnoreUnderRavenwood(blockedBy = ContentProvider.class) +public class FakeSettingsProviderTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); private MockContentResolver mCr; - @Override + @Before public void setUp() throws Exception { mCr = new MockContentResolver(); mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); } + @Test @SmallTest public void testBasicOperation() throws Exception { String settingName = Settings.System.SCREEN_BRIGHTNESS; diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 2237ba1924db..aaddf0e50ddd 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -169,6 +169,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "-1961637874": { + "message": "DeferredDisplayUpdater: applying DisplayInfo immediately", + "level": "DEBUG", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" + }, "-1949279037": { "message": "Attempted to add input method window with bad token %s. Aborting.", "level": "WARN", @@ -313,6 +319,12 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1818910559": { + "message": "DeferredDisplayUpdater: applied DisplayInfo after deferring", + "level": "DEBUG", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" + }, "-1814361639": { "message": "Set IME snapshot position: (%d, %d)", "level": "INFO", @@ -595,6 +607,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1518132958": { + "message": "fractionRendered boundsOverSource=%f", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "-1517908912": { "message": "requestScrollCapture: caught exception dispatching to window.token=%s", "level": "WARN", @@ -961,6 +979,12 @@ "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, + "-1209762265": { + "message": "Registering listener=%s with id=%d for window=%s with %s", + "level": "DEBUG", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "-1209252064": { "message": "Clear animatingExit: reason=clearAnimatingFlags win=%s", "level": "DEBUG", @@ -1333,6 +1357,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-888703350": { + "message": "Skipping %s", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "-883738232": { "message": "Adding more than one toast window for UID at a time.", "level": "WARN", @@ -1909,6 +1939,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-415346336": { + "message": "DeferredDisplayUpdater: partially applying DisplayInfo immediately", + "level": "DEBUG", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" + }, "-401282500": { "message": "destroyIfPossible: r=%s destroy returned removed=%s", "level": "DEBUG", @@ -1957,6 +1993,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "-376950429": { + "message": "DeferredDisplayUpdater: deferring DisplayInfo update", + "level": "DEBUG", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/DeferredDisplayUpdater.java" + }, "-374767836": { "message": "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s", "level": "VERBOSE", @@ -2803,6 +2845,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "360319850": { + "message": "fractionRendered scale=%f", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "364992694": { "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", "level": "VERBOSE", @@ -2983,6 +3031,12 @@ "group": "WM_DEBUG_BACK_PREVIEW", "at": "com\/android\/server\/wm\/BackNavigationController.java" }, + "532771960": { + "message": "Adding untrusted state listener=%s with id=%d", + "level": "DEBUG", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "535103992": { "message": "Wallpaper may change! Adjusting", "level": "VERBOSE", @@ -3061,6 +3115,12 @@ "group": "WM_DEBUG_DREAM", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "605179032": { + "message": "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "608694300": { "message": " NEW SURFACE SESSION %s", "level": "INFO", @@ -3289,6 +3349,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowState.java" }, + "824532141": { + "message": "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f minFractionRendered=%f", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "829434921": { "message": "Draw state now committed in %s", "level": "VERBOSE", @@ -3583,6 +3649,12 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, + "1090378847": { + "message": "Checking %d windows", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1100065297": { "message": "Attempted to get IME policy of a display that does not exist: %d", "level": "WARN", @@ -3715,6 +3787,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1251721200": { + "message": "unregister failed, couldn't find deathRecipient for %s with id=%d", + "level": "ERROR", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1252594551": { "message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d", "level": "WARN", @@ -3853,6 +3931,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, + "1382634842": { + "message": "Unregistering listener=%s with id=%d", + "level": "DEBUG", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1393721079": { "message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]", "level": "VERBOSE", @@ -3901,6 +3985,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1445704347": { + "message": "coveredRegionsAbove updated with %s frame:%s region:%s", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1448683958": { "message": "Override pending remote transitionSet=%b adapter=%s", "level": "INFO", @@ -4201,6 +4291,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "1786463281": { + "message": "Adding trusted state listener=%s with id=%d", + "level": "DEBUG", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1789321832": { "message": "Then token:%s is invalid. It might be removed", "level": "WARN", @@ -4375,6 +4471,12 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "1955470028": { + "message": "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s scale=%f,%f", + "level": "VERBOSE", + "group": "WM_DEBUG_TPL", + "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" + }, "1964565370": { "message": "Starting remote animation", "level": "INFO", @@ -4659,6 +4761,9 @@ "WM_DEBUG_TASKS": { "tag": "WindowManager" }, + "WM_DEBUG_TPL": { + "tag": "WindowManager" + }, "WM_DEBUG_WALLPAPER": { "tag": "WindowManager" }, diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 92c4de6490a3..f10cdb82022e 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1739,10 +1739,6 @@ public class Paint { /** * Get the elegant metrics flag. * - * From API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default value will be true by - * default if the app has a target SDK of API {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or - * later. - * * @return true if elegant metrics are enabled for text drawing. */ public boolean isElegantTextHeight() { diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java index b4b3e9275035..4ec5e1b67c5d 100644 --- a/keystore/java/android/security/Authorization.java +++ b/keystore/java/android/security/Authorization.java @@ -26,7 +26,6 @@ import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.StrictMode; import android.security.authorization.IKeystoreAuthorization; -import android.security.authorization.LockScreenEvent; import android.system.keystore2.ResponseCode; import android.util.Log; @@ -76,26 +75,37 @@ public class Authorization { } /** - * Informs keystore2 about lock screen event. + * Tells Keystore that the device is now unlocked for a user. * - * @param locked - whether it is a lock (true) or unlock (false) event - * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic - * password provided by the LockSettingService - * @param unlockingSids - KeyMint secure user IDs that should be permitted to unlock - * UNLOCKED_DEVICE_REQUIRED keys. + * @param userId - the user's Android user ID + * @param password - a secret derived from the user's synthetic password, if the unlock method + * is LSKF (or equivalent) and thus has made the synthetic password available + * @return 0 if successful or a {@code ResponseCode}. + */ + public static int onDeviceUnlocked(int userId, @Nullable byte[] password) { + StrictMode.noteDiskWrite(); + try { + getService().onDeviceUnlocked(userId, password); + return 0; + } catch (RemoteException | NullPointerException e) { + Log.w(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } catch (ServiceSpecificException e) { + return e.errorCode; + } + } + + /** + * Tells Keystore that the device is now locked for a user. * + * @param userId - the user's Android user ID + * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again * @return 0 if successful or a {@code ResponseCode}. */ - public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId, - @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) { + public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) { StrictMode.noteDiskWrite(); try { - if (locked) { - getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids); - } else { - getService().onLockScreenEvent( - LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids); - } + getService().onDeviceLocked(userId, unlockingSids); return 0; } catch (RemoteException | NullPointerException e) { Log.w(TAG, "Can not connect to keystore", e); diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 231fa4837441..4982f3732089 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -618,7 +618,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * @see #isMgf1DigestsSpecified() */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { if (mMgf1Digests.isEmpty()) { throw new IllegalStateException("Mask generation function (MGF) not specified"); @@ -633,7 +633,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * @see #getMgf1Digests() */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public boolean isMgf1DigestsSpecified() { return !mMgf1Digests.isEmpty(); } @@ -1292,7 +1292,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * <p>See {@link KeyProperties}.{@code DIGEST} constants. */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) { mMgf1Digests = Set.of(mgf1Digests); return this; diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index c1e3bab5d37c..7b6b2d142f95 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -401,7 +401,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { * @see #isMgf1DigestsSpecified() */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public @KeyProperties.DigestEnum Set<String> getMgf1Digests() { if (mMgf1Digests.isEmpty()) { throw new IllegalStateException("Mask generation function (MGF) not specified"); @@ -416,7 +416,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { * @see #getMgf1Digests() */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public boolean isMgf1DigestsSpecified() { return !mMgf1Digests.isEmpty(); } @@ -799,7 +799,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { * <p>See {@link KeyProperties}.{@code DIGEST} constants. */ @NonNull - @FlaggedApi("MGF1_DIGEST_SETTER") + @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER) public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) { mMgf1Digests = Set.of(mgf1Digests); return this; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index ed4b485f3927..9c05a3a768a0 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -28,6 +28,7 @@ import android.hardware.security.keymint.SecurityLevel; import android.hardware.security.keymint.Tag; import android.os.Build; import android.os.StrictMode; +import android.security.Flags; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore2; import android.security.KeyStoreException; @@ -853,6 +854,22 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest )); }); + + /* If the MGF1 Digest setter is not set, fall back to the previous behaviour: + * Add, as MGF1 Digest function, all the primary digests. + * Avoid adding the default MGF1 digest as it will have been included in the + * mKeymasterMgf1Digests field. + */ + if (!getMgf1DigestSetterFlag()) { + final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster( + DEFAULT_MGF1_DIGEST); + ArrayUtils.forEach(mKeymasterDigests, (digest) -> { + if (digest != defaultMgf1Digest) { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest)); + } + }); + } } }); ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> { @@ -928,6 +945,16 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato return params; } + private static boolean getMgf1DigestSetterFlag() { + try { + return Flags.mgf1DigestSetter(); + } catch (SecurityException e) { + Log.w(TAG, "Cannot read MGF1 Digest setter flag value", e); + return false; + } + } + + private void addAlgorithmSpecificParameters(List<KeyParameter> params) { switch (mKeymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_RSA: diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index ddbd93e458fd..2d8c5a380c6b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -25,6 +25,7 @@ import android.hardware.security.keymint.HardwareAuthenticatorType; import android.hardware.security.keymint.KeyParameter; import android.hardware.security.keymint.SecurityLevel; import android.os.StrictMode; +import android.security.Flags; import android.security.GateKeeper; import android.security.KeyStore2; import android.security.KeyStoreParameter; @@ -256,6 +257,15 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } } + private static boolean getMgf1DigestSetterFlag() { + try { + return Flags.mgf1DigestSetter(); + } catch (SecurityException e) { + Log.w(NAME, "Cannot read MGF1 Digest setter flag value", e); + return false; + } + } + @Override public Date engineGetCreationDate(String alias) { KeyEntryResponse response = getKeyMetadata(alias); @@ -537,11 +547,31 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { /* Because of default MGF1 digest is SHA-1. It has to be added in Key * characteristics. Otherwise, crypto operations will fail with Incompatible * MGF1 digest. + * If the MGF1 Digest setter flag isn't set, then the condition in the + * if clause above must be false (cannot have MGF1 digests specified if the + * flag was off). In that case, in addition to adding the default MGF1 + * digest, we have to add all the other digests as MGF1 Digests. + * */ importArgs.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST) )); + if (!getMgf1DigestSetterFlag()) { + final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster( + DEFAULT_MGF1_DIGEST); + for (String digest : spec.getDigests()) { + int digestToAddAsMgf1Digest = KeyProperties.Digest.toKeymaster( + digest); + // Do not add the default MGF1 digest as it has been added above. + if (digestToAddAsMgf1Digest != defaultMgf1Digest) { + importArgs.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, + digestToAddAsMgf1Digest + )); + } + } + } } } } diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml deleted file mode 100644 index ef3006042261..000000000000 --- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> -<shape android:shape="rectangle" - android:tintMode="multiply" - android:tint="@color/decor_title_color" - xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@android:color/white" /> -</shape> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml index c525a297b2e0..85bf2c1e4dca 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml @@ -21,8 +21,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" - android:orientation="horizontal" - android:background="@drawable/desktop_mode_decor_title"> + android:orientation="horizontal"> <LinearLayout android:id="@+id/open_menu_button" @@ -45,7 +44,6 @@ android:layout_width="0dp" android:layout_height="20dp" android:minWidth="80dp" - android:textColor="@color/desktop_mode_caption_app_name_dark" android:textAppearance="@android:style/TextAppearance.Material.Title" android:textSize="14sp" android:textFontWeight="500" @@ -62,7 +60,6 @@ android:layout_height="16dp" android:contentDescription="@string/expand_menu_text" android:src="@drawable/ic_baseline_expand_more_24" - android:tint="@color/desktop_mode_caption_expand_button_dark" android:background="@null" android:scaleType="fitCenter" android:clickable="false" @@ -87,8 +84,7 @@ android:src="@drawable/decor_desktop_mode_maximize_button_dark" android:scaleType="fitCenter" android:gravity="end" - android:background="@null" - android:tint="@color/desktop_mode_caption_maximize_button_dark"/> + android:background="@null"/> <ImageButton android:id="@+id/close_window" @@ -100,6 +96,5 @@ android:src="@drawable/decor_close_button_dark" android:scaleType="fitCenter" android:gravity="end" - android:background="@null" - android:tint="@color/desktop_mode_caption_close_button_dark"/> + android:background="@null"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml index 7638132d6562..cec7ee233236 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml @@ -20,8 +20,7 @@ android:id="@+id/desktop_mode_caption" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_horizontal" - android:background="@drawable/desktop_mode_decor_title"> + android:gravity="center_horizontal"> <ImageButton android:id="@+id/caption_handle" @@ -33,5 +32,4 @@ tools:tint="@color/desktop_mode_caption_handle_bar_dark" android:scaleType="fitXY" android:background="?android:selectableItemBackground"/> - </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 9bfd1b44dcca..fae71efe3b39 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -60,14 +60,6 @@ <!-- Desktop Mode --> <color name="desktop_mode_caption_handle_bar_light">#EFF1F2</color> <color name="desktop_mode_caption_handle_bar_dark">#1C1C17</color> - <color name="desktop_mode_caption_expand_button_light">#EFF1F2</color> - <color name="desktop_mode_caption_expand_button_dark">#48473A</color> - <color name="desktop_mode_caption_close_button_light">#EFF1F2</color> - <color name="desktop_mode_caption_close_button_dark">#1C1C17</color> - <color name="desktop_mode_caption_maximize_button_light">#EFF1F2</color> - <color name="desktop_mode_caption_maximize_button_dark">#1C1C17</color> - <color name="desktop_mode_caption_app_name_light">#EFF1F2</color> - <color name="desktop_mode_caption_app_name_dark">#1C1C17</color> <color name="desktop_mode_resize_veil_light">#EFF1F2</color> <color name="desktop_mode_resize_veil_dark">#1C1C17</color> <color name="desktop_mode_maximize_menu_button">#DDDACD</color> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index dc413b059fd7..a32b435ff99e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -28,7 +28,7 @@ import android.view.RemoteAnimationTarget; import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; -import com.android.internal.jank.InteractionJankMonitor; +import com.android.internal.jank.Cuj.CujType; import com.android.wm.shell.common.InteractionJankMonitorUtils; /** @@ -42,7 +42,7 @@ public class BackAnimationRunner { private final IOnBackInvokedCallback mCallback; private final IRemoteAnimationRunner mRunner; - private final @InteractionJankMonitor.CujType int mCujType; + private final @CujType int mCujType; private final Context mContext; // Whether we are waiting to receive onAnimationStart @@ -55,7 +55,7 @@ public class BackAnimationRunner { @NonNull IOnBackInvokedCallback callback, @NonNull IRemoteAnimationRunner runner, @NonNull Context context, - @InteractionJankMonitor.CujType int cujType) { + @CujType int cujType) { mCallback = callback; mRunner = runner; mCujType = cujType; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 85ea8097a2c1..7a3210e0a46d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -637,7 +637,7 @@ public class Bubble implements BubbleViewProvider { * @return the last time this bubble was updated or accessed, whichever is most recent. */ long getLastActivity() { - return isAppBubble() ? Long.MAX_VALUE : Math.max(mLastUpdated, mLastAccessed); + return Math.max(mLastUpdated, mLastAccessed); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index e74e578dc213..249f52bd6156 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1280,7 +1280,14 @@ public class BubbleController implements ConfigurationChangeListener, * Dismiss bubble if it exists and remove it from the stack */ public void dismissBubble(Bubble bubble, @Bubbles.DismissReason int reason) { - mBubbleData.dismissBubbleWithKey(bubble.getKey(), reason); + dismissBubble(bubble.getKey(), reason); + } + + /** + * Dismiss bubble with given key if it exists and remove it from the stack + */ + public void dismissBubble(String key, @Bubbles.DismissReason int reason) { + mBubbleData.dismissBubbleWithKey(key, reason); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 595a4afbfc86..bbb4b74c2a17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -784,8 +784,7 @@ public class BubbleData { if (bubble.getPendingIntentCanceled() || !(reason == Bubbles.DISMISS_AGED || reason == Bubbles.DISMISS_USER_GESTURE - || reason == Bubbles.DISMISS_RELOAD_FROM_DISK) - || bubble.isAppBubble()) { + || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) { return; } if (DEBUG_BUBBLE_DATA) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 50e1f7311ce0..d073f1df938a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -67,7 +67,6 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; - private BubbleBarExpandedViewDragController mDragController; private @Nullable Supplier<Rect> mLayerBoundsSupplier; private @Nullable Listener mListener; @@ -181,8 +180,6 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mHandleView.setOnClickListener(view -> { mMenuViewController.showMenu(true /* animated */); }); - - mDragController = new BubbleBarExpandedViewDragController(this); } public BubbleBarHandleView getHandleView() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt index 933794be071e..4ea18f78f5b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt @@ -19,29 +19,47 @@ package com.android.wm.shell.bubbles.bar import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.graphics.PointF +import android.graphics.Rect import android.view.MotionEvent import android.view.View import com.android.wm.shell.animation.Interpolators +import com.android.wm.shell.common.bubbles.DismissView import com.android.wm.shell.common.bubbles.RelativeTouchListener /** Controller for handling drag interactions with [BubbleBarExpandedView] */ -class BubbleBarExpandedViewDragController(private val expandedView: BubbleBarExpandedView) { +class BubbleBarExpandedViewDragController( + private val expandedView: BubbleBarExpandedView, + private val dismissView: DismissView, + private val onDismissed: () -> Unit +) { init { expandedView.handleView.setOnTouchListener(HandleDragListener()) } + private fun finishDrag(x: Float, y: Float, viewInitialX: Float, viewInitialY: Float) { + val dismissCircleBounds = Rect().apply { dismissView.circle.getBoundsOnScreen(this) } + if (dismissCircleBounds.contains(x.toInt(), y.toInt())) { + onDismissed() + } else { + resetExpandedViewPosition(viewInitialX, viewInitialY) + } + dismissView.hide() + } + private fun resetExpandedViewPosition(initialX: Float, initialY: Float) { - val listener = object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator) { - expandedView.isAnimating = true - } + val listener = + object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + expandedView.isAnimating = true + } - override fun onAnimationEnd(animation: Animator) { - expandedView.isAnimating = false + override fun onAnimationEnd(animation: Animator) { + expandedView.isAnimating = false + } } - } - expandedView.animate() + expandedView + .animate() .translationX(initialX) .translationY(initialY) .setDuration(RESET_POSITION_ANIM_DURATION) @@ -74,6 +92,7 @@ class BubbleBarExpandedViewDragController(private val expandedView: BubbleBarExp ) { expandedView.translationX = expandedViewRestPosition.x + dx expandedView.translationY = expandedViewRestPosition.y + dy + dismissView.show() } override fun onUp( @@ -86,11 +105,12 @@ class BubbleBarExpandedViewDragController(private val expandedView: BubbleBarExp velX: Float, velY: Float ) { - resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y) + finishDrag(ev.rawX, ev.rawY, expandedViewRestPosition.x, expandedViewRestPosition.y) } override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) { resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y) + dismissView.hide() } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 92cb436528cc..bdb0e206e490 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -31,17 +31,21 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.FrameLayout; +import com.android.wm.shell.R; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleOverflow; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; +import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.bubbles.DeviceConfig; +import com.android.wm.shell.bubbles.DismissViewUtils; +import com.android.wm.shell.common.bubbles.DismissView; + +import kotlin.Unit; import java.util.Objects; import java.util.function.Consumer; -import kotlin.Unit; - /** * Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window * manager to display bubbles. However, it is only used when bubbles are being displayed in @@ -63,7 +67,11 @@ public class BubbleBarLayerView extends FrameLayout @Nullable private BubbleViewProvider mExpandedBubble; + @Nullable private BubbleBarExpandedView mExpandedView; + @Nullable + private BubbleBarExpandedViewDragController mDragController; + private DismissView mDismissView; private @Nullable Consumer<String> mUnBubbleConversationCallback; // TODO(b/273310265) - currently the view is always on the right, need to update for RTL. @@ -101,6 +109,8 @@ public class BubbleBarLayerView extends FrameLayout mScrimView.setBackgroundDrawable(new ColorDrawable( getResources().getColor(android.R.color.system_neutral1_1000))); + setUpDismissView(); + setOnClickListener(view -> hideMenuOrCollapse()); } @@ -196,6 +206,13 @@ public class BubbleBarLayerView extends FrameLayout } }); + mDragController = new BubbleBarExpandedViewDragController(mExpandedView, mDismissView, + () -> { + mBubbleController.dismissBubble(mExpandedBubble.getKey(), + Bubbles.DISMISS_USER_GESTURE); + return Unit.INSTANCE; + }); + addView(mExpandedView, new FrameLayout.LayoutParams(width, height)); } @@ -227,6 +244,7 @@ public class BubbleBarLayerView extends FrameLayout mAnimationHelper.animateCollapse(() -> removeView(viewToRemove)); mBubbleController.getSysuiProxy().onStackExpandChanged(false); mExpandedView = null; + mDragController = null; setTouchDelegate(null); showScrim(false); } @@ -252,6 +270,18 @@ public class BubbleBarLayerView extends FrameLayout mUnBubbleConversationCallback = unBubbleConversationCallback; } + private void setUpDismissView() { + if (mDismissView != null) { + removeView(mDismissView); + } + mDismissView = new DismissView(getContext()); + DismissViewUtils.setup(mDismissView); + int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation); + + addView(mDismissView); + mDismissView.setElevation(elevation); + } + /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */ private void hideMenuOrCollapse() { if (mEducationViewController.isEducationVisible()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java index ec344d345139..86f00b83cadd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java @@ -23,6 +23,7 @@ import android.text.TextUtils; import android.view.SurfaceControl; import android.view.View; +import com.android.internal.jank.Cuj.CujType; import com.android.internal.jank.InteractionJankMonitor; /** Utils class for simplfy InteractionJank trancing call */ @@ -31,11 +32,11 @@ public class InteractionJankMonitorUtils { /** * Begin a trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link CujType}. * @param view the view to trace * @param tag the tag to distinguish different flow of same type CUJ. */ - public static void beginTracing(@InteractionJankMonitor.CujType int cujType, + public static void beginTracing(@CujType int cujType, @NonNull View view, @Nullable String tag) { final InteractionJankMonitor.Configuration.Builder builder = InteractionJankMonitor.Configuration.Builder.withView(cujType, view); @@ -48,12 +49,12 @@ public class InteractionJankMonitorUtils { /** * Begin a trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link CujType}. * @param context the context * @param surface the surface to trace * @param tag the tag to distinguish different flow of same type CUJ. */ - public static void beginTracing(@InteractionJankMonitor.CujType int cujType, + public static void beginTracing(@CujType int cujType, @NonNull Context context, @NonNull SurfaceControl surface, @Nullable String tag) { final InteractionJankMonitor.Configuration.Builder builder = InteractionJankMonitor.Configuration.Builder.withSurface(cujType, context, surface); @@ -66,18 +67,18 @@ public class InteractionJankMonitorUtils { /** * End a trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link CujType}. */ - public static void endTracing(@InteractionJankMonitor.CujType int cujType) { + public static void endTracing(@CujType int cujType) { InteractionJankMonitor.getInstance().end(cujType); } /** * Cancel the trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link CujType}. */ - public static void cancelTracing(@InteractionJankMonitor.CujType int cujType) { + public static void cancelTracing(@CujType int cujType) { InteractionJankMonitor.getInstance().cancel(cujType); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 38ce16489b06..d157ca837608 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -16,8 +16,8 @@ package com.android.wm.shell.onehanded; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION; +import static com.android.internal.jank.Cuj.CUJ_ONE_HANDED_ENTER_TRANSITION; +import static com.android.internal.jank.Cuj.CUJ_ONE_HANDED_EXIT_TRANSITION; import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT; import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER; @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.jank.Cuj.CujType; import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; @@ -327,7 +328,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { mTransitionCallbacks.add(callback); } - void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) { + void beginCUJTracing(@CujType int cujType, @Nullable String tag) { final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry = getDisplayAreaTokenMap().entrySet().iterator().next(); final InteractionJankMonitor.Configuration.Builder builder = @@ -339,11 +340,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { mJankMonitor.begin(builder); } - void endCUJTracing(@InteractionJankMonitor.CujType int cujType) { + void endCUJTracing(@CujType int cujType) { mJankMonitor.end(cujType); } - void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) { + void cancelCUJTracing(@CujType int cujType) { mJankMonitor.cancel(cujType); } 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 79c20761abed..0367ba160605 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 @@ -255,6 +255,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private ArrayList<TaskState> mOpeningTasks = null; private WindowContainerToken mPipTask = null; + private int mPipTaskId = -1; private WindowContainerToken mRecentsTask = null; private int mRecentsTaskId = -1; private TransitionInfo mInfo = null; @@ -822,8 +823,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } else if (!didMergeThings) { // Didn't recognize anything in incoming transition so don't merge it. Slog.w(TAG, "Don't know how to merge this transition, foundRecentsClosing=" - + foundRecentsClosing); - if (foundRecentsClosing) { + + foundRecentsClosing + " recentsTaskId=" + mRecentsTaskId); + if (foundRecentsClosing || mRecentsTaskId < 0) { mWillFinishToHome = false; cancel(false /* toHome */, false /* withScreenshots */, "didn't merge"); } @@ -904,12 +905,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Override public void setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.setFinishTaskTransaction: taskId=%d", - mInstanceId, taskId); mExecutor.execute(() -> { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.setFinishTaskTransaction: taskId=%d," + + " [mFinishCB is non-null]=%b", + mInstanceId, taskId, mFinishCB != null); if (mFinishCB == null) return; mPipTransaction = finishTransaction; + mPipTaskId = taskId; }); } @@ -1003,10 +1006,35 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // the leaf-tasks are not "independent" so aren't hidden by normal setup). t.hide(mPausingTasks.get(i).mTaskSurface); } - if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { - t.show(mInfo.getChange(mPipTask).getLeash()); - PictureInPictureSurfaceTransaction.apply(mPipTransaction, - mInfo.getChange(mPipTask).getLeash(), t); + if (mPipTransaction != null && sendUserLeaveHint) { + SurfaceControl pipLeash = null; + if (mPipTask != null) { + pipLeash = mInfo.getChange(mPipTask).getLeash(); + } else if (mPipTaskId != -1) { + // find a task with taskId from #setFinishTaskTransaction() + for (TransitionInfo.Change change : mInfo.getChanges()) { + if (change.getTaskInfo() != null + && change.getTaskInfo().taskId == mPipTaskId) { + pipLeash = change.getLeash(); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "RecentsController.finishInner:" + + " found a change with taskId=%d", mPipTaskId); + } + } + } + if (pipLeash == null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "RecentsController.finishInner: no valid PiP leash;" + + "mPipTransaction=%s, mPipTask=%s, mPipTaskId=%d", + mPipTransaction.toString(), mPipTask.toString(), mPipTaskId); + } else { + t.show(pipLeash); + PictureInPictureSurfaceTransaction.apply(mPipTransaction, pipLeash, t); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "RecentsController.finishInner: PiP transaction %s merged", + mPipTransaction); + } + mPipTaskId = -1; mPipTask = null; mPipTransaction = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 56f1c784f3a7..7b5709769369 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -847,9 +847,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1)) .orElse(null); if (taskInfo != null) { - startTask(taskInfo.taskId, position, options); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, - "Start task in background"); + if (ENABLE_SHELL_TRANSITIONS) { + mStageCoordinator.startTask(taskInfo.taskId, position, options); + } else { + startTask(taskInfo.taskId, position, options); + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background"); return; } if (samePackage(packageName1, packageName2, userId1, userId2)) { 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 be685b57f779..449bef5608c9 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 @@ -263,6 +263,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mStartIntent2 = startIntent2; mActivatePosition = position; } + SplitRequest(int taskId1, int position) { + mActivateTaskId = taskId1; + mActivatePosition = position; + } SplitRequest(int taskId1, int taskId2, int position) { mActivateTaskId = taskId1; mActivateTaskId2 = taskId2; @@ -556,6 +560,27 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + /** Use this method to launch an existing Task via a taskId */ + void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { + mSplitRequest = new SplitRequest(taskId, position); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + wct.startTask(taskId, options); + // If this should be mixed, send the task to avoid split handle transition directly. + if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(taskId, mTaskOrganizer)) { + mTaskOrganizer.applyTransaction(wct); + return; + } + + // If split screen is not activated, we're expecting to open a pair of apps to split. + final int extraTransitType = mMainStage.isActive() + ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; + prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); + + mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, + extraTransitType, !mIsDropEntering); + } + /** Launches an activity into split. */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { @@ -1593,7 +1618,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // 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); + if (!mIsDropEntering) { + mMainStage.evictAllChildren(wct); + } mMainStage.reparentTopTask(wct); prepareSplitLayout(wct, resizeAnim); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index ce7fef2d1fdf..9f20f49b4094 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -48,9 +48,9 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.common.split.SplitScreenUtils; -import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; @@ -298,7 +298,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); return handler.second; - } else if (mUnfoldHandler != null && mUnfoldHandler.hasUnfold(request)) { + } else if (mUnfoldHandler != null && mUnfoldHandler.shouldPlayUnfoldAnimation(request)) { final WindowContainerTransaction wct = mUnfoldHandler.handleRequest(transition, request); if (wct != null) { @@ -902,6 +902,18 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return false; } + /** Use to when split use taskId to enter, check if this enter transition should be mixed or + * not.*/ + public boolean shouldSplitEnterMixed(int taskId, ShellTaskOrganizer shellTaskOrganizer) { + // Check if this intent package is same as pip one or not, if true we want let the pip + // task enter split. + if (mPipHandler != null) { + return mPipHandler.isInPipPackage( + SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer)); + } + return false; + } + /** @return whether the transition-request represents a pip-entry. */ public boolean requestHasPipEnter(TransitionRequestInfo request) { return mPipHandler.requestHasPipEnter(request); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index 5c02dbcb5255..473deba3b7d2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -57,7 +57,9 @@ public class HomeTransitionObserver implements TransitionObserver, @NonNull SurfaceControl.Transaction finishTransaction) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || taskInfo.taskId == -1) { + if (taskInfo == null + || taskInfo.taskId == -1 + || !taskInfo.isRunning) { continue; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 07c54293111c..20ff79f7318e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -106,7 +106,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { - if (hasUnfold(info) && transition != mTransition) { + if (shouldPlayUnfoldAnimation(info) && transition != mTransition) { // Take over transition that has unfold, we might receive it if no other handler // accepted request in handleRequest, e.g. for rotation + unfold or // TRANSIT_NONE + unfold transitions @@ -213,14 +213,36 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene } /** Whether `request` contains an unfold action. */ - public boolean hasUnfold(@NonNull TransitionRequestInfo request) { + public boolean shouldPlayUnfoldAnimation(@NonNull TransitionRequestInfo request) { + // Unfold animation won't play when animations are disabled + if (!ValueAnimator.areAnimatorsEnabled()) return false; + return (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null - && request.getDisplayChange().isPhysicalDisplayChanged()); + && isUnfoldDisplayChange(request.getDisplayChange())); + } + + private boolean isUnfoldDisplayChange( + @NonNull TransitionRequestInfo.DisplayChange displayChange) { + if (!displayChange.isPhysicalDisplayChanged()) { + return false; + } + + if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) { + return false; + } + + // Handle only unfolding, currently we don't have an animation when folding + final int endArea = + displayChange.getEndAbsBounds().width() * displayChange.getEndAbsBounds().height(); + final int startArea = displayChange.getStartAbsBounds().width() + * displayChange.getStartAbsBounds().height(); + + return endArea > startArea; } /** Whether `transitionInfo` contains an unfold action. */ - public boolean hasUnfold(@NonNull TransitionInfo transitionInfo) { + public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) { // Unfold animation won't play when animations are disabled if (!ValueAnimator.areAnimatorsEnabled()) return false; @@ -250,7 +272,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (hasUnfold(request)) { + if (shouldPlayUnfoldAnimation(request)) { mTransition = transition; return new WindowContainerTransaction(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt index b739ad3793e4..589a8134c2d3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt @@ -1,14 +1,23 @@ package com.android.wm.shell.windowdecor.viewholder +import android.annotation.ColorInt import android.app.ActivityManager.RunningTaskInfo import android.content.res.ColorStateList +import android.content.res.Configuration import android.graphics.Bitmap -import android.graphics.drawable.GradientDrawable +import android.graphics.Color import android.view.View import android.view.View.OnLongClickListener import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import androidx.core.content.withStyledAttributes +import com.android.internal.R.attr.materialColorOnSecondaryContainer +import com.android.internal.R.attr.materialColorOnSurface +import com.android.internal.R.attr.materialColorSecondaryContainer +import com.android.internal.R.attr.materialColorSurfaceContainerHigh +import com.android.internal.R.attr.materialColorSurfaceContainerLow +import com.android.internal.R.attr.materialColorSurfaceDim import com.android.wm.shell.R /** @@ -49,57 +58,82 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( } override fun bindData(taskInfo: RunningTaskInfo) { - val captionDrawable = captionView.background as GradientDrawable - taskInfo.taskDescription?.statusBarColor?.let { - captionDrawable.setColor(it) - } - - closeWindowButton.imageTintList = ColorStateList.valueOf( - getCaptionCloseButtonColor(taskInfo)) - maximizeWindowButton.imageTintList = ColorStateList.valueOf( - getCaptionMaximizeButtonColor(taskInfo)) - expandMenuButton.imageTintList = ColorStateList.valueOf( - getCaptionExpandButtonColor(taskInfo)) - appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo)) + captionView.setBackgroundColor(getCaptionBackgroundColor(taskInfo)) + val color = getAppNameAndButtonColor(taskInfo) + val alpha = Color.alpha(color) + closeWindowButton.imageTintList = ColorStateList.valueOf(color) + maximizeWindowButton.imageTintList = ColorStateList.valueOf(color) + expandMenuButton.imageTintList = ColorStateList.valueOf(color) + appNameTextView.setTextColor(color) + appIconImageView.imageAlpha = alpha + maximizeWindowButton.imageAlpha = alpha + closeWindowButton.imageAlpha = alpha + expandMenuButton.imageAlpha = alpha } override fun onHandleMenuOpened() {} override fun onHandleMenuClosed() {} - private fun getCaptionAppNameTextColor(taskInfo: RunningTaskInfo): Int { - return if (shouldUseLightCaptionColors(taskInfo)) { - context.getColor(R.color.desktop_mode_caption_app_name_light) - } else { - context.getColor(R.color.desktop_mode_caption_app_name_dark) + @ColorInt + private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int { + val materialColorAttr: Int = + if (isDarkMode()) { + if (!taskInfo.isFocused) { + materialColorSurfaceContainerHigh + } else { + materialColorSurfaceDim + } + } else { + if (!taskInfo.isFocused) { + materialColorSurfaceContainerLow + } else { + materialColorSecondaryContainer + } } - } - - private fun getCaptionCloseButtonColor(taskInfo: RunningTaskInfo): Int { - return if (shouldUseLightCaptionColors(taskInfo)) { - context.getColor(R.color.desktop_mode_caption_close_button_light) - } else { - context.getColor(R.color.desktop_mode_caption_close_button_dark) + context.withStyledAttributes(null, intArrayOf(materialColorAttr), 0, 0) { + return getColor(0, 0) } + return 0 } - private fun getCaptionMaximizeButtonColor(taskInfo: RunningTaskInfo): Int { - return if (shouldUseLightCaptionColors(taskInfo)) { - context.getColor(R.color.desktop_mode_caption_maximize_button_light) - } else { - context.getColor(R.color.desktop_mode_caption_maximize_button_dark) + @ColorInt + private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int { + val materialColorAttr = when { + isDarkMode() -> materialColorOnSurface + else -> materialColorOnSecondaryContainer } + val appDetailsOpacity = when { + isDarkMode() && !taskInfo.isFocused -> DARK_THEME_UNFOCUSED_OPACITY + !isDarkMode() && !taskInfo.isFocused -> LIGHT_THEME_UNFOCUSED_OPACITY + else -> FOCUSED_OPACITY + } + context.withStyledAttributes(null, intArrayOf(materialColorAttr), 0, 0) { + val color = getColor(0, 0) + return if (appDetailsOpacity == FOCUSED_OPACITY) { + color + } else { + Color.argb( + appDetailsOpacity, + Color.red(color), + Color.green(color), + Color.blue(color) + ) + } + } + return 0 } - private fun getCaptionExpandButtonColor(taskInfo: RunningTaskInfo): Int { - return if (shouldUseLightCaptionColors(taskInfo)) { - context.getColor(R.color.desktop_mode_caption_expand_button_light) - } else { - context.getColor(R.color.desktop_mode_caption_expand_button_dark) - } + private fun isDarkMode(): Boolean { + return context.resources.configuration.uiMode and + Configuration.UI_MODE_NIGHT_MASK == + Configuration.UI_MODE_NIGHT_YES } companion object { private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder" + private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55% + private const val LIGHT_THEME_UNFOCUSED_OPACITY = 166 // 65% + private const val FOCUSED_OPACITY = 255 } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt index b1fb0f184bff..4930cb721336 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt @@ -2,9 +2,11 @@ package com.android.wm.shell.windowdecor.viewholder import android.animation.ObjectAnimator import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.res.ColorStateList -import android.graphics.drawable.GradientDrawable +import android.graphics.Color import android.view.View +import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import android.widget.ImageButton import com.android.wm.shell.R import com.android.wm.shell.animation.Interpolators @@ -34,10 +36,8 @@ internal class DesktopModeFocusedWindowDecorationViewHolder( override fun bindData(taskInfo: RunningTaskInfo) { taskInfo.taskDescription?.statusBarColor?.let { captionColor -> - val captionDrawable = captionView.background as GradientDrawable - captionDrawable.setColor(captionColor) + captionView.setBackgroundColor(captionColor) } - captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) } @@ -57,6 +57,22 @@ internal class DesktopModeFocusedWindowDecorationViewHolder( } } + /** + * Whether the caption items should use the 'light' color variant so that there's good contrast + * with the caption background color. + */ + private fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean { + return taskInfo.taskDescription + ?.let { taskDescription -> + if (Color.alpha(taskDescription.statusBarColor) != 0 && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { + Color.valueOf(taskDescription.statusBarColor).luminance() < 0.5 + } else { + taskDescription.statusBarAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0 + } + } ?: false + } + /** Animate appearance/disappearance of caption handle as the handle menu is animated. */ private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) { val animator = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt index 8b405f02ef29..690b4e4be122 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt @@ -1,11 +1,8 @@ package com.android.wm.shell.windowdecor.viewholder import android.app.ActivityManager.RunningTaskInfo -import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context -import android.graphics.Color import android.view.View -import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS /** * Encapsulates the root [View] of a window decoration and its children to facilitate looking up @@ -20,22 +17,6 @@ internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) { */ abstract fun bindData(taskInfo: RunningTaskInfo) - /** - * Whether the caption items should use the 'light' color variant so that there's good contrast - * with the caption background color. - */ - protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean { - return taskInfo.taskDescription - ?.let { taskDescription -> - if (Color.alpha(taskDescription.statusBarColor) != 0 && - taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { - Color.valueOf(taskDescription.statusBarColor).luminance() < 0.5 - } else { - taskDescription.statusBarAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0 - } - } ?: false - } - /** Callback when the handle menu is opened. */ abstract fun onHandleMenuOpened() diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt index d06cf775ca60..e272958d78f8 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt @@ -105,7 +105,8 @@ open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition ) locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true) mockLocationEnabled = true - mainHandler.post(updateLocation) + // postpone first location update to make sure GPS is set as test provider + mainHandler.postDelayed(updateLocation, 200) // normal app open through the Launcher All Apps // var mapsAddressOption = "Golden Gate Bridge" diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 4bca96b187b5..dab762f233e2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -221,6 +221,22 @@ public class BubbleDataTest extends ShellTestCase { } @Test + public void testAddAppBubble_setsTime() { + // Setup + mBubbleData.setListener(mListener); + + // Test + assertThat(mAppBubble.getLastActivity()).isEqualTo(0); + setCurrentTime(1000); + mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/, + false /* showInShade */); + + // Verify + assertThat(mBubbleData.getBubbleInStackWithKey(mAppBubble.getKey())).isEqualTo(mAppBubble); + assertThat(mAppBubble.getLastActivity()).isEqualTo(1000); + } + + @Test public void testRemoveBubble() { // Setup sendUpdatedEntryAtTime(mEntryA1, 1000); @@ -1162,7 +1178,7 @@ public class BubbleDataTest extends ShellTestCase { } @Test - public void test_removeAppBubble_skipsOverflow() { + public void test_removeAppBubble_overflows() { String appBubbleKey = mAppBubble.getKey(); mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/, false /* showInShade */); @@ -1170,7 +1186,7 @@ public class BubbleDataTest extends ShellTestCase { mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_USER_GESTURE); - assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull(); + assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isEqualTo(mAppBubble); assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isNull(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 99cd4f391153..855b7ee04702 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -235,7 +235,7 @@ public class SplitScreenControllerTests extends ShellTestCase { mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, SPLIT_POSITION_TOP_OR_LEFT, null); - verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), + verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), isNull()); verify(mSplitScreenController, never()).supportMultiInstancesSplit(any()); verify(mStageCoordinator, never()).switchSplitPosition(any()); @@ -243,7 +243,6 @@ public class SplitScreenControllerTests extends ShellTestCase { @Test public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() { - doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any()); doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any()); Intent startIntent = createStartIntent("startActivity"); PendingIntent pendingIntent = @@ -260,8 +259,8 @@ public class SplitScreenControllerTests extends ShellTestCase { mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, SPLIT_POSITION_TOP_OR_LEFT, null); - - verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), + verify(mSplitScreenController, never()).supportMultiInstancesSplit(any()); + verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), isNull()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index 421c44511a54..50802c3759c9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -101,7 +101,7 @@ public class HomeTransitionObserverTest extends ShellTestCase { when(change.getTaskInfo()).thenReturn(taskInfo); when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); - setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true); mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), info, @@ -119,7 +119,7 @@ public class HomeTransitionObserverTest extends ShellTestCase { when(change.getTaskInfo()).thenReturn(taskInfo); when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); - setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_TO_BACK); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_TO_BACK, true); mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), info, @@ -137,7 +137,25 @@ public class HomeTransitionObserverTest extends ShellTestCase { when(change.getTaskInfo()).thenReturn(taskInfo); when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); - setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_UNDEFINED, TRANSIT_TO_BACK); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_UNDEFINED, TRANSIT_TO_BACK, true); + + mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), + info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + + verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean()); + } + + @Test + public void testNonRunningHomeActivityDoesNotTriggerCallback() throws RemoteException { + TransitionInfo info = mock(TransitionInfo.class); + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); + when(change.getTaskInfo()).thenReturn(taskInfo); + when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); + + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_UNDEFINED, TRANSIT_TO_BACK, false); mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), info, @@ -156,7 +174,7 @@ public class HomeTransitionObserverTest extends ShellTestCase { when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true); - setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE, true); mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), info, @@ -166,16 +184,16 @@ public class HomeTransitionObserverTest extends ShellTestCase { verify(mListener, times(1)).onHomeVisibilityChanged(true); } - /** * Helper class to initialize variables for the rest. */ private void setupTransitionInfo(ActivityManager.RunningTaskInfo taskInfo, TransitionInfo.Change change, @ActivityType int activityType, - @TransitionMode int mode) { + @TransitionMode int mode, + boolean isRunning) { when(taskInfo.getActivityType()).thenReturn(activityType); when(change.getMode()).thenReturn(mode); + taskInfo.isRunning = isRunning; } - } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java index 6d73c12dc304..fc1fe1cd0acc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java @@ -98,10 +98,13 @@ public class UnfoldTransitionHandlerTest { } @Test - public void handleRequest_physicalDisplayChange_handlesTransition() { + public void handleRequest_physicalDisplayChangeUnfold_handlesTransition() { ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo(); TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange( - Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true); + Display.DEFAULT_DISPLAY) + .setPhysicalDisplayChanged(true) + .setStartAbsBounds(new Rect(0, 0, 100, 100)) + .setEndAbsBounds(new Rect(0, 0, 200, 200)); TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE, triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */); @@ -112,6 +115,23 @@ public class UnfoldTransitionHandlerTest { } @Test + public void handleRequest_physicalDisplayChangeFold_doesNotHandleTransition() { + ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo(); + TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange( + Display.DEFAULT_DISPLAY) + .setPhysicalDisplayChanged(true) + .setStartAbsBounds(new Rect(0, 0, 200, 200)) + .setEndAbsBounds(new Rect(0, 0, 100, 100)); + TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE, + triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */); + + WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition, + requestInfo); + + assertThat(result).isNull(); + } + + @Test public void handleRequest_noPhysicalDisplayChange_doesNotHandleTransition() { ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo(); TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange( @@ -306,7 +326,10 @@ public class UnfoldTransitionHandlerTest { private TransitionRequestInfo createUnfoldTransitionRequestInfo() { ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo(); TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange( - Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true); + Display.DEFAULT_DISPLAY) + .setPhysicalDisplayChanged(true) + .setStartAbsBounds(new Rect(0, 0, 100, 100)) + .setEndAbsBounds(new Rect(0, 0, 200, 200)); return new TransitionRequestInfo(TRANSIT_CHANGE, triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */); } diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp index 0275e4f13b3b..353300186555 100644 --- a/libs/hwui/jni/YuvToJpegEncoder.cpp +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -401,7 +401,7 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env, if (int success = jpegREncoder.encodeJPEGR(&p010, &yuv420, hdrTransferFunction, &jpegR, jpegQuality, - exif.length > 0 ? &exif : NULL); success != android::OK) { + exif.length > 0 ? &exif : NULL); success != JPEGR_NO_ERROR) { ALOGW("Encode JPEG/R failed, error code: %d.", success); return false; } diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 65e16056c106..bba9c9764eee 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -218,6 +218,15 @@ void PointerController::setPresentation(Presentation presentation) { mLocked.presentation = presentation; + if (input_flags::enable_pointer_choreographer()) { + // When pointer choreographer is enabled, the presentation mode is only set once when the + // PointerController is constructed, before the display viewport is provided. + // TODO(b/293587049): Clean up the PointerController interface after pointer choreographer + // is permanently enabled. The presentation can be set in the constructor. + mCursorController.setStylusHoverMode(presentation == Presentation::STYLUS_HOVER); + return; + } + if (!mCursorController.isViewportValid()) { return; } diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java index 32e636f8658b..5e3f8033d116 100644 --- a/location/java/android/location/GnssNavigationMessage.java +++ b/location/java/android/location/GnssNavigationMessage.java @@ -78,7 +78,7 @@ public final class GnssNavigationMessage implements Parcelable { public static final int TYPE_GAL_F = 0x0602; /** * NavIC L5 C/A message contained in the structure. - * @deprecated Use {@link #TYPE_IRN_L5} instead. + * @deprecated deprecated. */ @Deprecated public static final int TYPE_IRN_L5CA = 0x0701; diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 3c0b00262c70..07f63e5441af 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -62,3 +62,10 @@ flag { description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users." bug: "288580225" } + +flag { + name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name" + namespace: "media_solutions" + description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos." + bug: "314324170" +} diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml index 8d06765099a9..089f8989a30f 100644 --- a/packages/CredentialManager/res/values-el/strings.xml +++ b/packages/CredentialManager/res/values-el/strings.xml @@ -33,7 +33,7 @@ <string name="passwordless_technology_detail" msgid="6853928846532955882">"Τα κλειδιά πρόσβασης σάς επιτρέπουν να συνδέεστε χωρίς να εξαρτάστε από κωδικούς πρόσβασης. Χρειάζεται μόνο να χρησιμοποιήσετε το δακτυλικό αποτύπωμά σας, την αναγνώριση προσώπου, ένα PIN ή ένα μοτίβο σάρωσης για να επαληθεύσετε την ταυτότητά σας και να δημιουργήσετε ένα κλειδί πρόσβασης."</string> <string name="public_key_cryptography_title" msgid="6751970819265298039">"Κρυπτογραφία δημόσιου κλειδιού"</string> <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Βάσει του FIDO Alliance (συμμετ. οι Google, Apple, Microsoft κ.ά.) και προτύπων W3C, τα κλειδιά πρόσβ. χρησιμ. συζεύξεις κλειδιών κρυπτογρ. Σε αντίθεση με το όνομα χρήστη και τη συμβολοσειρά χαρακτ. για κωδ. πρόσβ., δημιουργείται μια σύζευξη ιδιωτικού-δημόσιου κλειδ. για εφαρμ./ιστοτόπους. Το ιδιωτ. κλειδί αποθηκεύεται με ασφάλεια στη συσκευή ή στον διαχειρ. κωδ. πρόσβ. και επιβεβαιώνει την ταυτότητά σας. Το δημόσιο κλειδί κοινοπ. στον διακομιστή εφαρμ./ιστοτόπου. Τα αντίστοιχα κλειδιά επιτρέπουν άμεση εγγραφή και σύνδεση."</string> - <string name="improved_account_security_title" msgid="1069841917893513424">"Βελτιωμένη ασφάλεια λογαριασμού"</string> + <string name="improved_account_security_title" msgid="1069841917893513424">"Βελτιωμένη προστασία λογαριασμού"</string> <string name="improved_account_security_detail" msgid="9123750251551844860">"Κάθε κλειδί συνδέεται αποκλειστικά με την εφαρμογή ή τον ιστότοπο για τον οποίο δημιουργήθηκε ώστε να μην συνδέεστε ποτέ κατά λάθος σε μη νόμιμες εφαρμογές ή ιστοτόπους. Επιπλέον, οι παραβιάσεις είναι πολύ πιο δύσκολες, επειδή οι διακομιστές διατηρούν μόνο δημόσια κλειδιά."</string> <string name="seamless_transition_title" msgid="5335622196351371961">"Απρόσκοπτη μετάβαση"</string> <string name="seamless_transition_detail" msgid="4475509237171739843">"Καθώς κινούμαστε προς ένα μέλλον χωρίς κωδικούς πρόσβασης, οι κωδικοί πρόσβασης θα εξακολουθούν να είναι διαθέσιμοι μαζί με τα κλειδιά πρόσβασης."</string> diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml index a3496caddd6d..cb76a28c8521 100644 --- a/packages/PackageInstaller/res/values-af/strings.xml +++ b/packages/PackageInstaller/res/values-af/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Hou <xliff:g id="SIZE">%1$s</xliff:g> se programdata."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Wil jy hierdie app uitvee?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Wil jy hierdie app deïnstalleer? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon sal ook uitgevee word."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Wil jy hierdie app van jou private ruimte deïnstalleer?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Voer tans deïnstallerings uit"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislukte deïnstallerings"</string> <string name="uninstalling" msgid="8709566347688966845">"Deïnstalleer tans …"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Stel <xliff:g id="APPNAME">%1$s</xliff:g> terug vanaf <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Hierdie app sal in die agtergrond begin aflaai"</string> <string name="restore" msgid="8460854736328970444">"Stel terug"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jy is vanlyn"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Hierdie app sal outomaties teruggestel word wanneer jy aan die internet gekoppel is"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Iets het skeefgeloop"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Kon nie hierdie app terugstel nie"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Te min berging"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Jy kan spasie op hierdie toestel beskikbaar maak om hierdie app terug te stel. Berging benodig: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Optrede vereis"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Volg die volgende stappe om hierdie app terug te stel"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is gedeaktiveer"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is gedeïnstalleer"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Jy sal <xliff:g id="INSTALLERNAME">%1$s</xliff:g> moet installeer om hierdie app terug te stel"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Gaan voort"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Vee berging uit"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Instellings"</string> + <string name="close" msgid="5214897374055647996">"Maak toe"</string> </resources> diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml index 9d0fd5328177..e58eafea65a1 100644 --- a/packages/PackageInstaller/res/values-am/strings.xml +++ b/packages/PackageInstaller/res/values-am/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"ከመተግበሪያ ውሂብ <xliff:g id="SIZE">%1$s</xliff:g> አቆይ።"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ይህን መተግበሪያ መሰረዝ ይፈልጋሉ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ይህን መተግበሪያ ማራገፍ ይፈልጋሉ? የተባዛውም <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ይሰረዛል።"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ይህን መተግበሪያ ከግል ቦታዎ ማራገፍ ይፈልጋሉ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"በማሄድ ላይ ያሉ ማራገፎች"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ያልተሳኩ ማራገፎች"</string> <string name="uninstalling" msgid="8709566347688966845">"በማራገፍ ላይ…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"ከ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ወደነበረበት <xliff:g id="APPNAME">%1$s</xliff:g> መልስ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ይህ መተግበሪያ በዳራ ማውረድ ይጀምራል።"</string> <string name="restore" msgid="8460854736328970444">"ወደነበረበት መልስ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ከመስመር ውጭ ነዎት"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"እርስዎ ከበይነመረቡ ጋር ሲገናኙ ይህ መተግበሪያ በራስ-ሰር ወደነበረበት ይመለሳል"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"የሆነ ስህተት ተከስቷል"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ይህን መተግበሪያ ወደነበረበት ለመመለስ እየተሞከረ ሳለ አንድ ችግር ነበር"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"በቂ ማከማቻ የለም"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ይህን መተግበሪያ ወደነበረበት ለመመለስ፣ በዚህ መሳሪያ ላይ ቦታን ማስለቀቅ ይችላሉ። ማከማቻ ያስፈልጋል፦ <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"የሚያስፈልግ እርምጃ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ይህን መተግበሪያ ወደነበረበት ለመመለስ ቀጣዮቹን ደረጃዎች ይከተሉ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ተሰናክሏል"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ተራግፏል"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ይህን መተግበሪያ ወደነበረበት ለመመለስ፣ <xliff:g id="INSTALLERNAME">%1$s</xliff:g>ን መጫን አለብዎት"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ቀጥል"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ማከማቻን አጽዳ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ቅንብሮች"</string> + <string name="close" msgid="5214897374055647996">"ዝጋ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml index f4c9581893f1..1edd9b137455 100644 --- a/packages/PackageInstaller/res/values-ar/strings.xml +++ b/packages/PackageInstaller/res/values-ar/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بـ <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"هل تريد حذف هذا التطبيق؟"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"هل تريد إلغاء تثبيت هذا التطبيق؟ سيتم أيضًا حذف نسخة \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\" الطبق الأصل."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"هل تريد إلغاء تثبيت هذا التطبيق من المساحة الخاصة؟"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string> <string name="uninstalling" msgid="8709566347688966845">"جارٍ إلغاء التثبيت…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"هل تريد استعادة التطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" من \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\"؟"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"سيبدأ تنزيل هذا التطبيق في الخلفية."</string> <string name="restore" msgid="8460854736328970444">"استعادة"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"لا يتوفر اتصال بالإنترنت"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ستتم استعادة هذا التطبيق تلقائيًا بمجرد اتصالك بالإنترنت."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"حدث خطأ"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"حدثت مشكلة أثناء محاولة استعادة هذا التطبيق."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"مساحة التخزين غير كافية"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"لاستعادة هذا التطبيق، يجب إخلاء بعض المساحة على هذا الجهاز. مساحة التخزين المطلوبة: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"مطلوب اتخاذ إجراء"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"يُرجى اتّباع الخطوات التالية لاستعادة هذا التطبيق."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> غير مفعّل"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> غير مثبَّت"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"لاستعادة هذا التطبيق، يجب تثبيت <xliff:g id="INSTALLERNAME">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"متابعة"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"محو مساحة التخزين"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"الإعدادات"</string> + <string name="close" msgid="5214897374055647996">"إغلاق"</string> </resources> diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml index 4d264c6b06c8..9bf9cb05cbdb 100644 --- a/packages/PackageInstaller/res/values-as/strings.xml +++ b/packages/PackageInstaller/res/values-as/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"এপৰ ডেটাৰ <xliff:g id="SIZE">%1$s</xliff:g> ৰাখক"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"আপুনি এই এপ্টো মচিব বিচাৰেনে?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"আপুনি এই এপ্টো আনইনষ্টল কৰিব বিচাৰেনে? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ৰ ক্ল’নো মচা হ’ব।"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"আপুনি আপোনাৰ ব্যক্তিগত স্পেচৰ পৰা এই এপ্টো আনইনষ্টল কৰিব বিচাৰেনে?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"আনইনষ্টল কৰি থকা হৈছে"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"যিবোৰ আনইনষ্টল পৰা নগ\'ল"</string> <string name="uninstalling" msgid="8709566347688966845">"আনইনষ্টল কৰি থকা হৈছে…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>ৰ পৰা <xliff:g id="APPNAME">%1$s</xliff:g> পুনঃস্থাপন কৰিবনে?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"এই এপ্টোৱে নেপথ্যত ডাউনল’ড কৰিবলৈ আৰম্ভ কৰিব"</string> <string name="restore" msgid="8460854736328970444">"পুনঃস্থাপন কৰক"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"আপুনি অফলাইন হৈ আছে"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"আপুনি ইণ্টাৰনেটৰ সৈতে সংযুক্ত হ’লে এই এপ্টো স্বয়ংক্ৰিয়ভাৱে পুনঃস্থাপন কৰা হ’ব"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"কিবা ভুল হ’ল"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"এই এপ্টো পুনঃস্থাপন কৰাত কিবা অসুবিধা হৈছিল"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ষ্ট’ৰেজত পৰ্যাপ্ত ঠাই নাই"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"এই এপ্টো পুনঃস্থাপন কৰিবলৈ, আপুনি এই ডিভাইচটোত ঠাই খালী কৰিব পাৰে। আৱশ্যকীয় ষ্ট’ৰেজ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"কাৰ্যব্যৱস্থা লোৱাৰ প্ৰয়োজন"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"এই এপ্টো পুনঃস্থাপন কৰিবলৈ পৰৱৰ্তী পদক্ষেপসমূহ অনুসৰণ কৰক"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> অক্ষম কৰা আছে"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> আনইনষ্টল কৰা হৈছে"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"এই এপ্টো পুনঃস্থাপন কৰিবলৈ, আপুনি <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ইনষ্টল কৰিব লাগিব"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"অব্যাহত ৰাখক"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ষ্ট’ৰেজ মচক"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ছেটিং"</string> + <string name="close" msgid="5214897374055647996">"বন্ধ কৰক"</string> </resources> diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml index 0807ef7dcb6c..64004e9486ce 100644 --- a/packages/PackageInstaller/res/values-az/strings.xml +++ b/packages/PackageInstaller/res/values-az/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Tətbiq datasının <xliff:g id="SIZE">%1$s</xliff:g> hissəsini saxlayın."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Bu tətbiq silinsin?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Bu tətbiqi sistemdən silmək istəyirsiniz? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kopya da silinəcək."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Bu tətbiqi şəxsi məkandan silmək istəyirsiniz?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"İşləyən sistemlər silinmələr"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Uğursuz olan sistemlər silinmələr"</string> <string name="uninstalling" msgid="8709566347688966845">"Sistemdən silinir..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ünvanından bərpa edilsin?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Bu tətbiq arxa fonda endirilməyə başlayacaq"</string> <string name="restore" msgid="8460854736328970444">"Bərpa edin"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Oflaynsınız"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"İnternetə qoşulanda bu tətbiq avtomatik bərpa ediləcək"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Xəta oldu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu tətbiqi bərpa edərkən problem oldu"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Kifayət qədər yaddaş yoxdur"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Bu tətbiqi bərpa etmək üçün bu cihazda yer boşalda bilərsiniz. Lazımi yaddaş: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Tədbir görmək tələb edilir"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Bu tətbiqi bərpa etmək üçün növbəti addımları izləyin"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> deaktiv edilib"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> sistemdən silinib"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Bu tətbiqi bərpa etmək üçün <xliff:g id="INSTALLERNAME">%1$s</xliff:g> quraşdırılmalıdır"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Davam edin"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Yaddaşı təmizləyin"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ayarlar"</string> + <string name="close" msgid="5214897374055647996">"Bağlayın"</string> </resources> diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml index 6b9cd047c11c..464b12014437 100644 --- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml +++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadrži <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Želite da izbrišete ovu aplikaciju?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Želite da deinstalirate ovu aplikaciju? Klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> će takođe biti izbrisan."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Želite da deinstalirate ovu aplikaciju iz privatnog prostora?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktivna deinstaliranja"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspela deinstaliranja"</string> <string name="uninstalling" msgid="8709566347688966845">"Deinstalira se…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Želite da vratite <xliff:g id="APPNAME">%1$s</xliff:g> iz <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Aplikacija će započeti preuzimanje u pozadini."</string> <string name="restore" msgid="8460854736328970444">"Vrati"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Oflajn ste"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ova aplikacija će biti vraćena automatski kada se povežete na internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Došlo je do greške"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema pri vraćanju ove aplikacije"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno memorijskog prostora"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Možete da oslobodite prostor na ovom uređaju da biste vratili ovu aplikaciju. Potreban memorijski prostor: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Treba da reagujete"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Pratite dalja uputstva da biste vratili ovu aplikaciju"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Onemogućen <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Deinstaliran <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Da biste vratili ovu aplikaciju, treba da instalirate <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Nastavi"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Obriši memorijski prostor"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Podešavanja"</string> + <string name="close" msgid="5214897374055647996">"Zatvori"</string> </resources> diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml index 9e6e6fd32f61..3977f228c672 100644 --- a/packages/PackageInstaller/res/values-be/strings.xml +++ b/packages/PackageInstaller/res/values-be/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Захаваць даныя праграмы (<xliff:g id="SIZE">%1$s</xliff:g>)."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Выдаліць гэту праграму?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Выдаліць гэту праграму? Таксама будзе выдалены клон \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\"."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Выдаліць гэту праграму з прыватнай аўдыторыі?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Актыўныя выдаленні"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Нявыкананыя выдаленні"</string> <string name="uninstalling" msgid="8709566347688966845">"Ідзе выдаленне…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Аднавіць праграму \"<xliff:g id="APPNAME">%1$s</xliff:g>\" адсюль: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Праграма пачне спампоўвацца ў фонавым рэжыме"</string> <string name="restore" msgid="8460854736328970444">"Аднавіць"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Вы па-за сеткай"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Гэта праграма будзе аўтаматычна адноўлена, калі прылада падключыцца да інтэрнэту"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Адбылася памылка"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Пры аднаўленні праграмы ўзнікла праблема"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Не хапае месца ў сховішчы"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Каб аднавіць гэту праграму, вызваліце месца на прыладзе. Неабходны аб\'ём месца ў сховішчы: <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Патрабуецца дзеянне"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Каб аднавіць гэту праграму, выканайце далейшыя інструкцыі"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Усталёўшчык \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\" адключаны"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Усталёўшчык \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\" выдалены"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Каб аднавіць гэту праграму, усталюйце праграму \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\""</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Працягнуць"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Ачысціць сховішча"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Налады"</string> + <string name="close" msgid="5214897374055647996">"Закрыць"</string> </resources> diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml index 1c9187404a58..a5f34a769c7d 100644 --- a/packages/PackageInstaller/res/values-bg/strings.xml +++ b/packages/PackageInstaller/res/values-bg/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Запазване на <xliff:g id="SIZE">%1$s</xliff:g> данни от приложението."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Искате ли да изтриете това приложение?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Искате ли да деинсталирате това приложение? Копието на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> също ще бъде изтрито."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Искате ли да деинсталирате това приложение от личното си пространство?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активни деинсталирания"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспешни деинсталирания"</string> <string name="uninstalling" msgid="8709566347688966845">"Деинсталира се..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Да се възстанови ли <xliff:g id="APPNAME">%1$s</xliff:g> от <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Това приложение ще започне да се изтегля на заден план"</string> <string name="restore" msgid="8460854736328970444">"Възстановяване"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлайн сте"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Това приложение ще бъде възстановено автоматично, когато имате връзка с интернет"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Нещо се обърка"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"При опита за възстановяване на това приложение възникна проблем"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Няма достатъчно място в хранилището"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"За да възстановите това приложение, ще трябва да освободите място на устройството. Необходимо място в хранилището: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Изисква се действие"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Изпълнете следващите стъпки, за да възстановите това приложение"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> е деактивирано"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> е деинсталирано"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"За да възстановите това приложение, ще трябва да инсталирате <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Напред"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Изчистване на данните"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Настройки"</string> + <string name="close" msgid="5214897374055647996">"Затваряне"</string> </resources> diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml index 5a78f37751e4..eef339ac2635 100644 --- a/packages/PackageInstaller/res/values-bn/strings.xml +++ b/packages/PackageInstaller/res/values-bn/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"অ্যাপ ডেটার মধ্যে <xliff:g id="SIZE">%1$s</xliff:g> রেখে দিন।"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"আপনি এই অ্যাপ মুছে ফেলতে চান?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"আপনি এই অ্যাপ আনইনস্টল করতে চান? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ক্লোনও মুছে ফেলা হবে।"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"আপনার ব্যক্তিগত স্পেস থেকে এই অ্যাপ আনইনস্টল করতে চান?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"আনইনস্টল করা হচ্ছে"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"আনইনস্টল করা যায়নি"</string> <string name="uninstalling" msgid="8709566347688966845">"আনইনস্টল করা হচ্ছে…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> থেকে <xliff:g id="APPNAME">%1$s</xliff:g> ফিরিয়ে আনবেন?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"এই অ্যাপটি ব্যাকগ্রাউন্ডে ডাউনলোড হওয়া শুরু হবে"</string> <string name="restore" msgid="8460854736328970444">"ফিরিয়ে আনুন"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"আপনি অফলাইন আছেন"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"আপনি ইন্টারনেটের সাথে কানেক্ট থাকাকালীন এই অ্যাপ অটোমেটিক ফিরিয়ে আনা হবে"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"কোনও সমস্যা হয়েছে"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"এই অ্যাপ ফিরিয়ে আনতে চেষ্টা করার সময় সমস্যা হয়েছে"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"পর্যাপ্ত স্টোরেজ নেই"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"এই অ্যাপ ফিরিয়ে আনতে, এই ডিভাইসের স্পেস খালি করতে পারেন। স্টোরেজ প্রয়োজন: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ব্যবস্থা নিতে হবে"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"এই অ্যাপ ফিরিয়ে আনতে পরবর্তী ধাপগুলি ফলো করুন"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> বন্ধ করা হয়েছে"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> আনইনস্টল করা হয়েছে"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"এই অ্যাপ ফিরিয়ে আনতে, আপনাকে <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ইনস্টল করতে হবে"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"চালিয়ে যান"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"স্টোরেজ মুছুন"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"সেটিংস"</string> + <string name="close" msgid="5214897374055647996">"বন্ধ করুন"</string> </resources> diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml index aed2d22a9211..783884313b9a 100644 --- a/packages/PackageInstaller/res/values-bs/strings.xml +++ b/packages/PackageInstaller/res/values-bs/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadržati <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Želite li izbrisati ovu aplikaciju?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Želite li deinstalirati ovu aplikaciju? Izbrisat će se i klon aplikacije <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Želite li deinstalirati ovu aplikaciju iz privatnog prostora?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Tekuća deinstaliranja"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspjela deinstaliranja"</string> <string name="uninstalling" msgid="8709566347688966845">"Deinstaliranje..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vratiti aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g> s usluge <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Preuzimanje aplikacije će započeti u pozadini"</string> <string name="restore" msgid="8460854736328970444">"Vrati"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Offline ste"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Aplikacija će se automatski vratiti kada se povežete s internetom"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nešto nije uredu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema prilikom pokušaja vraćanja aplikacije"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno prostora za pohranu"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Da vratite aplikaciju, možete osloboditi prostor na uređaju. Potrebna pohrana: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Potrebna je radnja"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Pratite sljedeće korake da vratite ovu aplikaciju"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Program za instaliranje <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je onemogućen"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Program za instaliranje <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je deinstaliran"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Da vratite aplikaciju, trebat ćete instalirati program za instaliranje <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Nastavi"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Obriši pohranu"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Postavke"</string> + <string name="close" msgid="5214897374055647996">"Zatvori"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml index 85e5110d4405..bf28e8130205 100644 --- a/packages/PackageInstaller/res/values-ca/strings.xml +++ b/packages/PackageInstaller/res/values-ca/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Conserva <xliff:g id="SIZE">%1$s</xliff:g> de dades de l\'aplicació."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vols suprimir aquesta aplicació?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vols desinstal·lar aquesta aplicació? El clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> també se suprimirà."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vols desinstal·lar aquesta aplicació del teu espai privat?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstal·lacions en curs"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstal·lacions fallides"</string> <string name="uninstalling" msgid="8709566347688966845">"S\'està desinstal·lant…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vols restaurar <xliff:g id="APPNAME">%1$s</xliff:g> des de <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Aquesta aplicació començarà a baixar-se en segon pla"</string> <string name="restore" msgid="8460854736328970444">"Restaura"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tens connexió"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"L\'aplicació es restaurarà automàticament quan tinguis connexió a Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"S\'ha produït un error"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hi ha hagut un problema en intentar restaurar aquesta aplicació"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hi ha prou emmagatzematge"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Per restaurar l\'aplicació, allibera espai en aquest dispositiu. Emmagatzematge necessari: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Acció necessària"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Segueix els passos que hi ha continuació per restaurar l\'aplicació"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> està desactivat"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> no està instal·lat"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Per restaurar l\'aplicació, cal que instal·lis <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continua"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Esborra emmagatzematge"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Configuració"</string> + <string name="close" msgid="5214897374055647996">"Tanca"</string> </resources> diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml index fc85bca3bcc5..5c0ff241fd56 100644 --- a/packages/PackageInstaller/res/values-cs/strings.xml +++ b/packages/PackageInstaller/res/values-cs/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Ponechat data aplikace o velikosti <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Chcete tuto aplikaci smazat?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Chcete tuto aplikaci odinstalovat? Smazán bude také klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Chcete tuto aplikaci odinstalovat ze soukromého prostoru?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Probíhající odinstalace"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neúspěšné odinstalace"</string> <string name="uninstalling" msgid="8709566347688966845">"Odinstalace…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Obnovit aplikaci <xliff:g id="APPNAME">%1$s</xliff:g> z aplikace <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Aplikace se začne stahovat na pozadí"</string> <string name="restore" msgid="8460854736328970444">"Obnovit"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Nejste připojeni k internetu"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Tato aplikace se automaticky obnoví, až budete připojeni k internetu"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Něco se pokazilo"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Při pokusu o obnovení této aplikace došlo k problému"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nedostatek úložného prostoru"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Pokud tuto aplikaci chcete obnovit, uvolněte na zařízení místo. Požadované úložiště: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Vyžadovaná akce"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Pokud tuto aplikaci chcete obnovit, postupujte podle následujících pokynů"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> je zakázán"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> je odinstalován"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Pokud tuto aplikaci chcete obnovit, bude potřeba nainstalovat <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Pokračovat"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Vymazat úložiště"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Nastavení"</string> + <string name="close" msgid="5214897374055647996">"Zavřít"</string> </resources> diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml index aa6fe8f8baa0..acb888a3fc87 100644 --- a/packages/PackageInstaller/res/values-da/strings.xml +++ b/packages/PackageInstaller/res/values-da/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Behold <xliff:g id="SIZE">%1$s</xliff:g> appdata."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vil du slette denne app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vil du afinstallere denne app? Klonen af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> slettes også."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vil du afinstallere denne app fra dit private område?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Igangværende afinstallationer"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislykkede afinstallationer"</string> <string name="uninstalling" msgid="8709566347688966845">"Afinstallerer…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vil du gendanne <xliff:g id="APPNAME">%1$s</xliff:g> fra <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Download af denne app startes i baggrunden"</string> <string name="restore" msgid="8460854736328970444">"Gendan"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du er offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Denne app gendannes automatisk, når du har forbindelse til internettet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Noget gik galt"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Der opstod et problem under gendannelsen af denne app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ikke nok lagerplads"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Hvis du vil gendanne denne app, skal du frigøre plads på enheden. Påkrævet lagerplads: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Handling påkrævet"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Følg de næste trin for at gendanne denne app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> er deaktiveret"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> er afinstalleret"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Du skal installere <xliff:g id="INSTALLERNAME">%1$s</xliff:g> for at gendanne denne app"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Fortsæt"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Ryd lagerplads"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Indstillinger"</string> + <string name="close" msgid="5214897374055647996">"Luk"</string> </resources> diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml index dc2369610cf7..faecc3e9792d 100644 --- a/packages/PackageInstaller/res/values-de/strings.xml +++ b/packages/PackageInstaller/res/values-de/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> an App-Daten behalten."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Möchtest du diese App löschen?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Möchtest du diese App deinstallieren? Der <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-Klon wird ebenfalls gelöscht."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Möchtest du diese App in deinem privaten Bereich deinstallieren?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Laufende Deinstallationen"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Fehlgeschlagene Deinstallationen"</string> <string name="uninstalling" msgid="8709566347688966845">"Wird deinstalliert..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> von <xliff:g id="INSTALLERNAME">%1$s</xliff:g> wiederherstellen?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Diese App beginnt im Hintergrund mit dem Herunterladen"</string> <string name="restore" msgid="8460854736328970444">"Wiederherstellen"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du bist offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Die App wird automatisch wiederhergestellt, sobald du mit dem Internet verbunden bist"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ein Fehler ist aufgetreten"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Beim Wiederherstellen der App ist ein Fehler aufgetreten"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nicht genügend Speicherplatz"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Um die App wiederherzustellen, musst du erst Speicherplatz auf diesem Gerät freigeben. Erforderlicher Speicherplatz: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Aktion erforderlich"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Folge den nächsten Schritten, um die App wiederherzustellen"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ist deaktiviert"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ist deinstalliert"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Um die App wiederherzustellen, musst du den <xliff:g id="INSTALLERNAME">%1$s</xliff:g> installieren"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Weiter"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Speicherinhalt löschen"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Einstellungen"</string> + <string name="close" msgid="5214897374055647996">"Schließen"</string> </resources> diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml index ea4eeb68d2c1..f5deac9aa9c5 100644 --- a/packages/PackageInstaller/res/values-el/strings.xml +++ b/packages/PackageInstaller/res/values-el/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Διατήρηση <xliff:g id="SIZE">%1$s</xliff:g> δεδομένων εφαρμογών."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Θέλετε να διαγράψετε αυτή την εφαρμογή;"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή; Θα διαγραφεί επίσης το διπλότυπο της εφαρμογής <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Θέλετε να απεγκαταστήσετε αυτή την εφαρμογή από τον ιδιωτικό χώρο;"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Απεγκαταστάσεις σε εξέλιξη"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Αποτυχημένες απεγκαταστάσεις"</string> <string name="uninstalling" msgid="8709566347688966845">"Απεγκατάσταση…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Επαναφορά <xliff:g id="APPNAME">%1$s</xliff:g> από <xliff:g id="INSTALLERNAME">%1$s</xliff:g>;"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Η λήψη της εφαρμογής θα ξεκινήσει στο παρασκήνιο"</string> <string name="restore" msgid="8460854736328970444">"Επαναφορά"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Είστε εκτός σύνδεσης"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Θα γίνει αυτόματη επαναφορά αυτής της εφαρμογής όταν είστε συνδεδεμένοι στο διαδίκτυο"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Κάτι πήγε στραβά"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Παρουσιάστηκε κάποιο πρόβλημα κατά την επαναφορά αυτής της εφαρμογής"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Δεν επαρκεί ο αποθηκευτικός χώρος"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Για να επαναφέρετε αυτή την εφαρμογή, μπορείτε να ελευθερώσετε χώρο στη συσκευή. Απαιτούμενος αποθηκευτικός χώρος: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Απαιτούμενη ενέργεια"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Ακολουθήστε τα επόμενα βήματα για να επαναφέρετε αυτή την εφαρμογή"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Το <xliff:g id="INSTALLERNAME">%1$s</xliff:g> είναι απενεργοποιημένο"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Το <xliff:g id="INSTALLERNAME">%1$s</xliff:g> έχει απεγκατασταθεί"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Για να επαναφέρετε αυτή την εφαρμογή, πρέπει να εγκαταστήσετε το <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Συνέχεια"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Διαγραφή αποθηκευτικού χώρου"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ρυθμίσεις"</string> + <string name="close" msgid="5214897374055647996">"Κλείσιμο"</string> </resources> diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml index 01871720605e..4ad4fa12bea7 100644 --- a/packages/PackageInstaller/res/values-en-rAU/strings.xml +++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Do you want to delete this app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Do you want to uninstall this app? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone will also be deleted."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Do you want to uninstall this app from your private space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string> <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restore <xliff:g id="APPNAME">%1$s</xliff:g> from <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string> <string name="restore" msgid="8460854736328970444">"Restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"This app will automatically restore when you\'re connected to the Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"To restore this app, you can free up space on this device. Storage required: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action required"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Follow the next steps to restore this app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is disabled"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uninstalled"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"To restore this app, you\'ll need to install <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continue"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Clear storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"Close"</string> </resources> diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml index 9b31f27bcd4a..831ee1b4f442 100644 --- a/packages/PackageInstaller/res/values-en-rCA/strings.xml +++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Do you want to delete this app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Do you want to uninstall this app? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone will also be deleted."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Do you want to uninstall this app from your private space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstalls"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstalls"</string> <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restore <xliff:g id="APPNAME">%1$s</xliff:g> from <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"This app will begin to download in the background"</string> <string name="restore" msgid="8460854736328970444">"Restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"This app will automatically restore when you\'re connected to the internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"To restore this app, you can free up space on this device. Storage required: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action required"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Follow the next steps to restore this app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is disabled"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uninstalled"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"To restore this app, you\'ll need to install <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continue"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Clear storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"Close"</string> </resources> diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml index 01871720605e..4ad4fa12bea7 100644 --- a/packages/PackageInstaller/res/values-en-rGB/strings.xml +++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Do you want to delete this app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Do you want to uninstall this app? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone will also be deleted."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Do you want to uninstall this app from your private space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string> <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restore <xliff:g id="APPNAME">%1$s</xliff:g> from <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string> <string name="restore" msgid="8460854736328970444">"Restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"This app will automatically restore when you\'re connected to the Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"To restore this app, you can free up space on this device. Storage required: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action required"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Follow the next steps to restore this app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is disabled"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uninstalled"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"To restore this app, you\'ll need to install <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continue"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Clear storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"Close"</string> </resources> diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml index 01871720605e..4ad4fa12bea7 100644 --- a/packages/PackageInstaller/res/values-en-rIN/strings.xml +++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Do you want to delete this app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Do you want to uninstall this app? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone will also be deleted."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Do you want to uninstall this app from your private space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstallations"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstallations"</string> <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restore <xliff:g id="APPNAME">%1$s</xliff:g> from <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string> <string name="restore" msgid="8460854736328970444">"Restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"This app will automatically restore when you\'re connected to the Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"To restore this app, you can free up space on this device. Storage required: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action required"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Follow the next steps to restore this app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is disabled"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uninstalled"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"To restore this app, you\'ll need to install <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continue"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Clear storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"Close"</string> </resources> diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml index 67ffce594557..9ed871eb316a 100644 --- a/packages/PackageInstaller/res/values-en-rXC/strings.xml +++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Keep <xliff:g id="SIZE">%1$s</xliff:g> of app data."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Do you want to delete this app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Do you want to uninstall this app? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> clone will also be deleted."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Do you want to uninstall this app from your private space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Running uninstalls"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Failed uninstalls"</string> <string name="uninstalling" msgid="8709566347688966845">"Uninstalling…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restore <xliff:g id="APPNAME">%1$s</xliff:g> from <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"This app will begin to download in the background"</string> <string name="restore" msgid="8460854736328970444">"Restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"This app will automatically restore when you\'re connected to the internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"To restore this app, you can free up space on this device. Storage required: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action required"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Follow the next steps to restore this app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is disabled"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uninstalled"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"To restore this app, you\'ll need to install <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continue"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Clear storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"Close"</string> </resources> diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml index 2a8559a605ba..631875eef73b 100644 --- a/packages/PackageInstaller/res/values-es-rUS/strings.xml +++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Guardar <xliff:g id="SIZE">%1$s</xliff:g> en datos de apps"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"¿Quieres borrar esta app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"¿Quieres desinstalar esta app? También se borrará el clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"¿Quieres desinstalar esta app de tu espacio privado?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalaciones activas"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalaciones con errores"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"¿Quieres restablecer <xliff:g id="APPNAME">%1$s</xliff:g> desde <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Esta app comenzará la descarga en segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restablecer"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tienes conexión"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Se restablecerá automáticamente esta app cuando te conectes a Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Se produjo un error"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hubo un problema al intentar restablecer esta app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hay suficiente espacio de almacenamiento"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para restablecer la app, deberás liberar espacio en este dispositivo. Almacenamiento requerido: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Acción obligatoria"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Sigue los próximos pasos para restablecer esta app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Se inhabilitó <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"No se instaló <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para restablecer esta app, deberás instalar <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Liberar almacenamiento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Configuración"</string> + <string name="close" msgid="5214897374055647996">"Cerrar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml index 5e1022bf4c5a..bf85728c9090 100644 --- a/packages/PackageInstaller/res/values-es/strings.xml +++ b/packages/PackageInstaller/res/values-es/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantener <xliff:g id="SIZE">%1$s</xliff:g> de datos de aplicaciones."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"¿Quieres eliminar esta aplicación?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"¿Quieres desinstalar esta aplicación? El clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> también se eliminará."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"¿Quieres desinstalar esta aplicación de tu espacio privado?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalaciones en curso"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalaciones fallidas"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"¿Restaurar <xliff:g id="APPNAME">%1$s</xliff:g> de <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Esta aplicación comenzará a descargarse en segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restaurar"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tienes conexión a Internet"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Esta aplicación se restaurará automáticamente cuando tengas conexión a Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Se ha producido un error"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"No se ha podido restaurar esta aplicación"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hay suficiente espacio"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para restaurar esta aplicación, libera espacio del dispositivo. Almacenamiento necesario: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Acción necesaria"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Sigue los pasos que se indican a continuación para restaurar esta aplicación"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> está inhabilitada"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> está desinstalada"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para restaurar esta aplicación, tendrás que instalar <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Borrar almacenamiento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ajustes"</string> + <string name="close" msgid="5214897374055647996">"Cerrar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml index 2bd2c04574da..db3c7dcb9402 100644 --- a/packages/PackageInstaller/res/values-et/strings.xml +++ b/packages/PackageInstaller/res/values-et/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Säilita rakenduse andmete hulk <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Kas soovite selle rakenduse kustutada?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Kas soovite selle rakenduse desinstallida? Rakenduse <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> kloon kustutatakse samuti."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Kas soovite selle rakenduse oma privaatsest ruumist desinstallida?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Käimasolevad desinstallimised"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ebaõnnestunud desinstallimised"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstallimine …"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Kas taastada rakendus <xliff:g id="APPNAME">%1$s</xliff:g> rakenduse <xliff:g id="INSTALLERNAME">%1$s</xliff:g> kaudu?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Selle rakenduse allalaadimine algab taustal"</string> <string name="restore" msgid="8460854736328970444">"Taasta"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Võrguühendus puudub"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"See rakendus taastatakse automaatselt, kui olete Internetiga ühendatud"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Midagi läks valesti"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Selle rakenduse taastamisel ilmnes probleem"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Pole piisavalt salvestusruumi"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Selle rakenduse taastamiseks saate selles seadmes ruumi vabastada. Vajalik salvestusruum: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Toiming on vajalik"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Selle rakenduse taastamiseks järgige järgmisi samme"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> on keelatud"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> on desinstallitud"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Selle rakenduse taastamiseks peate installima: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Jätka"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Tühjenda salvestusruum"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Seaded"</string> + <string name="close" msgid="5214897374055647996">"Sule"</string> </resources> diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml index 574d4ab69374..e6fe660a9aee 100644 --- a/packages/PackageInstaller/res/values-eu/strings.xml +++ b/packages/PackageInstaller/res/values-eu/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantendu aplikazioetako datuen <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Aplikazioa ezabatu nahi duzu?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Aplikazioa desinstalatu nahi duzu? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> aplikazioaren klona ere ezabatuko da."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Aplikazioa eremu pribatutik desinstalatu nahi duzu?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Abian diren desinstalatze-eragiketak"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalatu ezin izan direnak"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalatzen…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> aplikaziotik leheneratu nahi duzu <xliff:g id="APPNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Atzeko planoan deskargatuko da aplikazioa"</string> <string name="restore" msgid="8460854736328970444">"Leheneratu"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ez zaude konektatuta Internetera"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Aplikazioa automatikoki leheneratuko da Internetera konektatzen zarenean"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Arazoren bat izan da"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Arazo bat izan da aplikazioa leheneratzean"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ez dago behar adina toki"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Aplikazioa leheneratzeko, egin tokia gailuan. Behar den tokia: <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Zerbait egin behar duzu"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Aplikazioa leheneratzeko, egin hurrengo urratsak"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> desgaituta dago"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ez dago instalatuta"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Aplikazioa leheneratzeko, <xliff:g id="INSTALLERNAME">%1$s</xliff:g> instalatu beharko duzu"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Egin aurrera"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Egin tokia"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ezarpenak"</string> + <string name="close" msgid="5214897374055647996">"Itxi"</string> </resources> diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml index 1869cb622d35..014dd93980bf 100644 --- a/packages/PackageInstaller/res/values-fa/strings.xml +++ b/packages/PackageInstaller/res/values-fa/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> از دادههای برنامه را نگهدارید."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"میخواهید این برنامه را حذف کنید؟"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"میخواهید این برنامه را حذف نصب کنید؟ همسانه <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> هم حذف خواهد شد."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"میخواهید این برنامه از فضای خصوصی حذف نصب شود؟"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"حذفنصبهای درحال انجام"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"حذفنصبهای ناموفق"</string> <string name="uninstalling" msgid="8709566347688966845">"درحال حذف نصب..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> از <xliff:g id="INSTALLERNAME">%1$s</xliff:g> بازیابی شود؟"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"این برنامه در پسزمینه شروع به بارگیری میکند"</string> <string name="restore" msgid="8460854736328970444">"بازیابی"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"آفلاین هستید"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"وقتی به اینترنت متصل شوید، این برنامه بهطور خودکار بازیابی خواهد شد"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"مشکلی پیش آمد"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"هنگام بازیابی این برنامه مشکلی پیش آمد"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"فضای ذخیرهسازی کافی نیست"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"برای بازیابی این برنامه، میتوانید فضای این دستگاه را آزاد کنید. فضای ذخیرهسازی لازم: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"اقدام لازم است"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"برای بازیابی این برنامه، مراحل بعدی را دنبال کنید"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> غیرفعال است"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> حذف نصب شده است"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"برای بازیابی این برنامه، باید <xliff:g id="INSTALLERNAME">%1$s</xliff:g> را نصب کنید"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ادامه دادن"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"پاک کردن فضای ذخیرهسازی"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"تنظیمات"</string> + <string name="close" msgid="5214897374055647996">"بستن"</string> </resources> diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml index 8f960ea50166..78e4067e7be0 100644 --- a/packages/PackageInstaller/res/values-fi/strings.xml +++ b/packages/PackageInstaller/res/values-fi/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Säilytä <xliff:g id="SIZE">%1$s</xliff:g> sovellusdataa"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Haluatko poistaa tämän sovelluksen?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Haluatko poistaa tämän sovelluksen? Myös klooni (<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>) poistetaan."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Haluatko poistaa tämän sovelluksen yksityisestä tilasta?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Käynnissä olevat poistot"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Epäonnistuneet poistot"</string> <string name="uninstalling" msgid="8709566347688966845">"Poistetaan…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Palautetaanko <xliff:g id="APPNAME">%1$s</xliff:g> sovelluksella <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Sovelluksen lataus aloitetaan taustalla"</string> <string name="restore" msgid="8460854736328970444">"Palauta"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Olet offline-tilassa"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Sovellus palautuu automaattiseti, kun olet yhteydessä internetiin"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Jotain meni pieleen"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Sovelluksen palauttamisessa oli ongelma"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Tallennustila ei riitä"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Voit palauttaa sovelluksen vapauttamalla tilaa laitteella. Vaadittu tallennustila: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Toimi nyt"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Seuraa vaiheita sovelluksen palauttamiseen"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> on poistettu käytöstä"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Asennus on poistettu (<xliff:g id="INSTALLERNAME">%1$s</xliff:g>)"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Asenna <xliff:g id="INSTALLERNAME">%1$s</xliff:g>, jotta voit palauttaa sovelluksen"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Jatka"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Tyhjennä tallennustila"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Asetukset"</string> + <string name="close" msgid="5214897374055647996">"Sulje"</string> </resources> diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml index 647a7b5a6141..8415b511b337 100644 --- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml +++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Garder <xliff:g id="SIZE">%1$s</xliff:g> de données d\'application."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Voulez-vous supprimer cette application?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Voulez-vous désinstaller cette application? Le clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sera aussi supprimé."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Voulez-vous désinstaller cette application de votre Espace privé?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Désinstallations en cours…"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Désinstallations échouées"</string> <string name="uninstalling" msgid="8709566347688966845">"Désinstallation en cours…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restaurer <xliff:g id="APPNAME">%1$s</xliff:g> à partir de <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Le téléchargement de cette application commencera en arrière-plan"</string> <string name="restore" msgid="8460854736328970444">"Restaurer"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Vous êtes hors ligne"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Cette application sera automatiquement restaurée lorsque vous serez connecté à Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Un problème est survenu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Un problème est survenu lors de la restauration de cette application"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Espace de stockage insuffisant"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Pour restaurer cette application, vous devez libérer de l\'espace de stockage sur cet appareil. Espace de stockage requis : <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action requise"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Suivez les étapes suivantes pour restaurer cette application"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> est désactivé"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> est désinstallé"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Pour restaurer cette application, vous devrez installer <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuer"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Effacer le stockage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Paramètres"</string> + <string name="close" msgid="5214897374055647996">"Fermer"</string> </resources> diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml index 3cf043b9c353..a90fdce1582a 100644 --- a/packages/PackageInstaller/res/values-fr/strings.xml +++ b/packages/PackageInstaller/res/values-fr/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Conserver <xliff:g id="SIZE">%1$s</xliff:g> de données d\'application."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Voulez-vous supprimer cette appli ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Voulez-vous désinstaller cette appli ? Le clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> sera supprimé aussi."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Souhaitez-vous désinstaller cette application de votre Espace privé ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Désinstallations en cours"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Échec des désinstallations"</string> <string name="uninstalling" msgid="8709566347688966845">"Désinstallation…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restaurer <xliff:g id="APPNAME">%1$s</xliff:g> depuis <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Cette application commencera à se télécharger en arrière-plan"</string> <string name="restore" msgid="8460854736328970444">"Restaurer"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Vous n\'êtes pas connecté"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"L\'application sera automatiquement restaurée lorsque vous serez connecté à Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Un problème est survenu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Un problème est survenu lors de la restauration de cette application"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Espace de stockage insuffisant"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Pour restaurer cette application, vous pouvez libérer de l\'espace sur cet appareil. Espace de stockage requis : <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Action requise"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Suivre la procédure ci-dessous pour restaurer cette application"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> est désactivé"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> n\'est pas installé"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Pour restaurer cette application, vous devrez installer <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuer"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Vider l\'espace de stockage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Paramètres"</string> + <string name="close" msgid="5214897374055647996">"Fermer"</string> </resources> diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml index eeb1f953a8a9..a758e32d98ce 100644 --- a/packages/PackageInstaller/res/values-gl/strings.xml +++ b/packages/PackageInstaller/res/values-gl/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Conservar os datos da aplicación, que ocupan <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Queres eliminar esta aplicación?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Queres desinstalar esta aplicación? Tamén se eliminará o clon de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Queres desinstalar esta aplicación do teu espazo privado?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalacións en curso"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Erros nas desinstalacións"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Queres restaurar <xliff:g id="APPNAME">%1$s</xliff:g> desde <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Esta aplicación comezará a descargarse en segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restaurar"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Estás sen conexión"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Esta aplicación restaurarase automaticamente cando te conectes a Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Produciuse un erro"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Produciuse un problema ao restaurar esta aplicación"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Non hai almacenamento suficiente"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para restaurar esta aplicación, podes liberar espazo neste dispositivo. Espazo de almacenamento necesario: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Acción necesaria"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Completa os pasos seguintes para restaurar esta aplicación"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Desactivouse <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Desinstalouse <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para restaurar esta aplicación, tes que instalar <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Liberar almacenamento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Configuración"</string> + <string name="close" msgid="5214897374055647996">"Pechar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml index e1f188724b16..4873303d7e3f 100644 --- a/packages/PackageInstaller/res/values-gu/strings.xml +++ b/packages/PackageInstaller/res/values-gu/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g>નો ઍપ ડેટા રાખો."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"શું તમે આ ઍપ ડિલીટ કરવા માગો છો?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માગો છો? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>ની ક્લોન પણ ડિલીટ કરવામાં આવશે."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"શું તમે તમારી ખાનગી સ્પેસમાંથી આ ઍપ અનઇન્સ્ટૉલ કરવા માગો છો?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ચાલી રહેલા અનઇન્સ્ટૉલ"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"નિષ્ફળ થયેલા અનઇન્સ્ટૉલ"</string> <string name="uninstalling" msgid="8709566347688966845">"અનઇન્સ્ટૉલ કરી રહ્યાં છીએ…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>માંથી <xliff:g id="APPNAME">%1$s</xliff:g> રિસ્ટોર કરીએ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"બૅકગ્રાઉન્ડમાં આ ઍપ ડાઉનલોડ થવાનું શરૂ થશે"</string> <string name="restore" msgid="8460854736328970444">"રિસ્ટોર કરો"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"તમે ઑફલાઇન છો"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"જ્યારે તમે ઇન્ટરનેટ સાથે કનેક્ટેડ થશો, ત્યારે આ ઍપ ઑટોમૅટિક રીતે રિસ્ટોર થશે"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"કંઈક ખોટું થયું"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"આ ઍપને રિસ્ટોર કરવામાં કોઈ સમસ્યા આવી હતી"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"પર્યાપ્ત સ્ટોરેજ નથી"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"આ ઍપને રિસ્ટોર કરવા માટે, તમે આ ડિવાઇસ પર સ્પેસ ખાલી કરી શકો છો. આવશ્યક સ્ટોરેજ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"પગલું આવશ્યક છે"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"આ ઍપને રિસ્ટોર કરવા માટે આગળના પગલાં અનુસરો"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> બંધ છે"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> અનઇન્સ્ટૉલ કર્યું છે"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"આ ઍપને રિસ્ટોર કરવા માટે, તમારે <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ઇન્સ્ટૉલ કરવું જરૂરી છે"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"આગળ વધો"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"સ્ટોરેજ સાફ કરો"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"સેટિંગ"</string> + <string name="close" msgid="5214897374055647996">"બંધ કરો"</string> </resources> diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml index 642bbd9baff3..1e2dea2a8e36 100644 --- a/packages/PackageInstaller/res/values-hi/strings.xml +++ b/packages/PackageInstaller/res/values-hi/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ऐप्लिकेशन डेटा रखें."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"क्या आपको यह ऐप्लिकेशन मिटाना है?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"क्या आपको यह ऐप्लिकेशन अनइंस्टॉल करना है? इससे <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> का क्लोन भी मिट जाएगा."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"क्या आपको अपने प्राइवेट स्पेस से यह ऐप्लिकेशन अनइंस्टॉल करना है?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"वे अनइंस्टॉल जो चल रहे हैं"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"वे अनइंस्टॉल जो सफल नहीं रहे"</string> <string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल हो रहा है…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> को <xliff:g id="INSTALLERNAME">%1$s</xliff:g> से वापस लाएं?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"यह ऐप्लिकेशन, बैकग्राउंड में डाउनलोड होना शुरू हो जाएगा"</string> <string name="restore" msgid="8460854736328970444">"वापस लाएं"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"आप ऑफ़लाइन हैं"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"इंटरनेट कनेक्शन होने पर, यह ऐप्लिकेशन अपने-आप वापस आ जाएगा"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"कोई गड़बड़ी हुई"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"इस ऐप्लिकेशन को वापस लाने में समस्या हुई"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"डिवाइस का स्टोरेज ज़रूरत से कम है"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"इस ऐप्लिकेशन को वापस लाने के लिए, इस डिवाइस में स्टोरेज खाली करें. इतने स्टोरेज की ज़रूरत है: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"कार्रवाई ज़रूरी है"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"इस ऐप्लिकेशन को वापस लाने के लिए, दिया गया तरीका अपनाएं"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> बंद है"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> अनइंस्टॉल है"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"इस ऐप्लिकेशन को वापस लाने के लिए, आपको <xliff:g id="INSTALLERNAME">%1$s</xliff:g> इंस्टॉल करना होगा"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"जारी रखें"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"स्टोरेज खाली करें"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Settings"</string> + <string name="close" msgid="5214897374055647996">"बंद करें"</string> </resources> diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml index 69ce3f13fec8..34901a48f429 100644 --- a/packages/PackageInstaller/res/values-hr/strings.xml +++ b/packages/PackageInstaller/res/values-hr/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Zadrži <xliff:g id="SIZE">%1$s</xliff:g> podataka aplikacije."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Želite li izbrisati ovu aplikaciju?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Želite li deinstalirati ovu aplikaciju? Klon za <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> također će se izbrisati."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Želite li deinstalirati ovu aplikaciju iz svojeg privatnog prostora?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Deinstaliranja u tijeku"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspjela deinstaliranja"</string> <string name="uninstalling" msgid="8709566347688966845">"Deinstaliranje…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Želite li vratiti aplikaciju <xliff:g id="APPNAME">%1$s</xliff:g> putem aplikacije <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Ova aplikacija počet će se preuzimati u pozadini"</string> <string name="restore" msgid="8460854736328970444">"Vrati"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Niste povezani s internetom"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Aplikacija će se automatski vratiti kad se povežete s internetom"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nešto nije u redu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema s vraćanjem aplikacije"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno prostora za pohranu"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Da biste vratili aplikaciju, možete osloboditi prostor za pohranu na ovom uređaju. Potreban prostor za pohranu: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Potrebna je radnja"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Da biste vratili aplikaciju, slijedite upute u nastavku"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> – onemogućeno"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> – deinstalirano"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Da biste vratili aplikaciju, instalirajte sljedeće: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Nastavi"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Izbriši pohranu"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Postavke"</string> + <string name="close" msgid="5214897374055647996">"Zatvori"</string> </resources> diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml index ea8cfc782f72..e8735266e258 100644 --- a/packages/PackageInstaller/res/values-hu/strings.xml +++ b/packages/PackageInstaller/res/values-hu/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> alkalmazásadat megtartása."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Szeretné törölni ezt az alkalmazást?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Eltávolítja ezt az alkalmazást? A klónozott <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> is törlésre kerül."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Eltávolítja ezt az alkalmazást a privát területről?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Futó eltávolítások"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Sikertelen telepítések"</string> <string name="uninstalling" msgid="8709566347688966845">"Eltávolítás…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Visszaállítja a(z) <xliff:g id="APPNAME">%1$s</xliff:g> appot innen: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"A háttérben megkezdődik az app letöltése"</string> <string name="restore" msgid="8460854736328970444">"Visszaállítás"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Az eszköz offline állapotban van"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Automatikusan megtörténik az alkalmazás visszaállítása, amikor Ön csatlakozik az internethez."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Hiba történt"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hiba történt az alkalmazás visszaállítása során."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nincs elegendő tárhely"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Az alkalmazás visszaállításához szabadítson fel tárhelyet ezen az eszközön. Szükséges tárhely: <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Fontos teendő"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Az alkalmazás visszaállításához kövesse a következő lépéseket."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"A(z) <xliff:g id="INSTALLERNAME">%1$s</xliff:g> le van tiltva"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"A(z) <xliff:g id="INSTALLERNAME">%1$s</xliff:g> nincs telepítve"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Az alkalmazás app visszaállításához telepítse a következőt: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Tovább"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Tárhely ürítése"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Beállítások"</string> + <string name="close" msgid="5214897374055647996">"Bezárás"</string> </resources> diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml index a447371c8ca4..cccf6b657510 100644 --- a/packages/PackageInstaller/res/values-hy/strings.xml +++ b/packages/PackageInstaller/res/values-hy/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Չհեռացնել հավելվածների տվյալները (<xliff:g id="SIZE">%1$s</xliff:g>):"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ջնջե՞լ այս հավելվածը"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ապատեղադրե՞լ այս հավելվածը։ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-ի կլոնը նույնպես կջնջվի։"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Ապատեղադրե՞լ այս հավելվածը ձեր անձնական տարածքից"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Ընթացիկ ապատեղադրումներ"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ձախողված ապատեղադրումներ"</string> <string name="uninstalling" msgid="8709566347688966845">"Ապատեղադրվում է…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Վերականգնե՞լ <xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը <xliff:g id="INSTALLERNAME">%1$s</xliff:g>-ից"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Այս հավելվածը կներբեռնվի ֆոնային ռեժիմում"</string> <string name="restore" msgid="8460854736328970444">"Վերականգնել"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Կապ չկա"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Այս հավելվածն ավտոմատ կվերականգնվի, երբ միանաք ինտերնետին"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Սխալ առաջացավ"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Չհաջողվեց վերականգնել այս հավելվածը"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Բավարար տարածք չկա"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Հավելվածը վերականգնելու համար տարածք ազատեք սարքում։ Պահանջվում է <xliff:g id="BYTES">%1$s</xliff:g>։"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Պահանջվում է գործողություն"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Այս հավելվածը վերականգնելու համար կատարեք հաջորդ քայլերը"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> հավելվածն անջատված է"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> հավելվածն ապատեղադրված է"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Այս հավելվածը վերականգնելու համար տեղադրեք <xliff:g id="INSTALLERNAME">%1$s</xliff:g> հավելվածը"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Շարունակել"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Մաքրել պահոցը"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Կարգավորումներ"</string> + <string name="close" msgid="5214897374055647996">"Փակել"</string> </resources> diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml index 9e24424d9360..5ab5649fab19 100644 --- a/packages/PackageInstaller/res/values-in/strings.xml +++ b/packages/PackageInstaller/res/values-in/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Pertahankan data aplikasi sebesar <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ingin menghapus aplikasi ini?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ingin meng-uninstal aplikasi ini? Clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> juga akan dihapus."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Ingin meng-uninstal aplikasi ini dari ruang pribadi?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Menjalankan proses uninstal"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Proses uninstal yang gagal"</string> <string name="uninstalling" msgid="8709566347688966845">"Meng-uninstal..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Pulihkan <xliff:g id="APPNAME">%1$s</xliff:g> dari <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Aplikasi ini akan mulai didownload di latar belakang"</string> <string name="restore" msgid="8460854736328970444">"Pulihkan"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Anda offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Aplikasi ini akan otomatis dipulihkan saat Anda terhubung ke internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Terjadi error"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Terjadi error saat mencoba memulihkan aplikasi ini"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Penyimpanan tidak cukup"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Untuk memulihkan aplikasi ini, Anda perlu mengosongkan ruang penyimpanan di perangkat ini. Penyimpanan yang diperlukan: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Tindakan diperlukan"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Ikuti langkah berikutnya untuk memulihkan aplikasi ini"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> dinonaktifkan"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> diuninstal"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Untuk memulihkan aplikasi ini, Anda harus menginstal <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Lanjutkan"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Hapus penyimpanan"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Setelan"</string> + <string name="close" msgid="5214897374055647996">"Tutup"</string> </resources> diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml index c24f2844a77a..3822994272cd 100644 --- a/packages/PackageInstaller/res/values-is/strings.xml +++ b/packages/PackageInstaller/res/values-is/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Halda <xliff:g id="SIZE">%1$s</xliff:g> af forritagögnum."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Viltu eyða þessu forriti?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Viltu fjarlægja þetta forrit? Afriti af <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> verður einnig eytt."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Viltu fjarlægja þetta forrit úr einkarýminu?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Fjarlægingar í gangi"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Fjarlægingar sem mistókust"</string> <string name="uninstalling" msgid="8709566347688966845">"Fjarlægir…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Endurheimta <xliff:g id="APPNAME">%1$s</xliff:g> úr <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Forritið verður sótt í bakgrunni"</string> <string name="restore" msgid="8460854736328970444">"Endurheimta"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Þú ert ekki á netinu"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Þetta forrit verður endurheimt sjálfkrafa þegar þú tengist netinu"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Eitthvað fór úrskeiðis"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Vandamál kom upp við að endurheimta þetta forrit"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ekki nógu mikið geymslurými"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Til að endurheimta þetta forrit geturðu losað um pláss í þessu tæki. Geymslurýmið sem þarf: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Aðgerðar krafist"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Fylgdu næstu skrefum til að endurheimta þetta forrit"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Slökkt er á <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Búið er að fjarlægja <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Þú þarft að setja upp <xliff:g id="INSTALLERNAME">%1$s</xliff:g> til að endurheimta þetta forrit"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Halda áfram"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Hreinsa geymslu"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Stillingar"</string> + <string name="close" msgid="5214897374055647996">"Loka"</string> </resources> diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml index 604d88ac4e1d..669a8db80c8f 100644 --- a/packages/PackageInstaller/res/values-it/strings.xml +++ b/packages/PackageInstaller/res/values-it/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Mantieni <xliff:g id="SIZE">%1$s</xliff:g> di dati delle app."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vuoi eliminare questa app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vuoi disinstallare questa app? Verrà eliminato anche il clone di <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vuoi disinstallare questa app dal tuo spazio privato?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Disinstallazioni in esecuzione"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Disinstallazioni non riuscite"</string> <string name="uninstalling" msgid="8709566347688966845">"Disinstallazione…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Ripristinare <xliff:g id="APPNAME">%1$s</xliff:g> da <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Il download di quest\'app inizierà in background"</string> <string name="restore" msgid="8460854736328970444">"Ripristina"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Sei offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Quest\'app verrà ripristinata automaticamente quando il dispositivo è connesso a internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Si è verificato un problema"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Si è verificato un problema durante il ripristino di questa app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Spazio di archiviazione insufficiente"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Per ripristinare quest\'app, puoi liberare spazio sul dispositivo. Spazio di archiviazione necessario: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Azione richiesta"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Segui i passaggi successivi per ripristinare quest\'app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> non è attivo"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> è stato disinstallato"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Per ripristinare quest\'app, dovrai installare <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continua"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Libera spazio di archiviazione"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Impostazioni"</string> + <string name="close" msgid="5214897374055647996">"Chiudi"</string> </resources> diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml index 0e37b2ebd39c..1030e03e19b4 100644 --- a/packages/PackageInstaller/res/values-iw/strings.xml +++ b/packages/PackageInstaller/res/values-iw/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"שמירת <xliff:g id="SIZE">%1$s</xliff:g> מנתוני האפליקציה."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"למחוק את האפליקציה הזו?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"להסיר את ההתקנה של האפליקציה הזו? השכפול של <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> גם יימחק."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"להסיר את ההתקנה של האפליקציה הזו מהמרחב הפרטי?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"התקנות בתהליכי הסרה"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"הסרות התקנה שנכשלו"</string> <string name="uninstalling" msgid="8709566347688966845">"בתהליך הסרת התקנה..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"לשחזר את <xliff:g id="APPNAME">%1$s</xliff:g> מ-<xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"תהליך ההורדה של האפליקציה יתחיל ברקע"</string> <string name="restore" msgid="8460854736328970444">"שחזור"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"אין חיבור לאינטרנט"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"שחזור האפליקציה יתחיל אוטומטית כשהמכשיר יחובר לאינטרנט"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"משהו השתבש"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"הייתה בעיה בניסיון לשחזר את האפליקציה הזו"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"אין מספיק נפח אחסון פנוי"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"כדי לשחזר את האפליקציה הזו צריך לפנות מקום במכשיר. נפח האחסון הנדרש: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"נדרשת פעולה"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"כדי לשחזר את האפליקציה הזו יש לפעול לפי השלבים הבאים"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> במצב מושבת"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"ההתקנה של <xliff:g id="INSTALLERNAME">%1$s</xliff:g> הוסרה"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"כדי לשחזר את האפליקציה הזו, צריך להתקין את <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"המשך"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"צריך לפנות נפח אחסון"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"הגדרות"</string> + <string name="close" msgid="5214897374055647996">"סגירה"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml index 58e46a61f872..80b60ffe8f9f 100644 --- a/packages/PackageInstaller/res/values-ja/strings.xml +++ b/packages/PackageInstaller/res/values-ja/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"アプリのデータ(<xliff:g id="SIZE">%1$s</xliff:g>)を保持"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"このアプリを削除しますか?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"このアプリをアンインストールしますか?<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> のクローンも削除されます。"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"このアプリをプライベート スペースからアンインストールしますか?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"アンインストールを実行しています"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"エラーになったアンインストール"</string> <string name="uninstalling" msgid="8709566347688966845">"アンインストールしています…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> から <xliff:g id="APPNAME">%1$s</xliff:g> を復元しますか?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"このアプリのダウンロードをバックグラウンドで開始します"</string> <string name="restore" msgid="8460854736328970444">"復元する"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"オフラインです"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"インターネットに接続すると、このアプリは自動的に復元されます"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"エラーが発生しました"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"このアプリを復元しようとしたときに問題が発生しました"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"空き容量が不足しています"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"このアプリを復元するには、このデバイスの空き容量を増やしてください。必要なストレージ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"必要な対応"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"このアプリを復元するには、次の手順を行います"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> は無効です"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> はアンインストールされています"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"このアプリを復元するには、<xliff:g id="INSTALLERNAME">%1$s</xliff:g> をインストールする必要があります"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"続行"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ストレージの消去"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"設定"</string> + <string name="close" msgid="5214897374055647996">"閉じる"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml index bbd63d168072..cfe90500d291 100644 --- a/packages/PackageInstaller/res/values-ka/strings.xml +++ b/packages/PackageInstaller/res/values-ka/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"შენარჩუნდეს აპების მონაცემების <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"გსურთ ამ აპის წაშლა?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"გსურთ ამ აპის დეინსტალაცია? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> კლონი ასევე წაიშლება."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"გსურთ ამ აპის დეინსტალაცია თქვენი პირადი სივრციდან?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"გაშვებული დეინსტალაციები"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"შეუსრულებელი დეინსტალაციები"</string> <string name="uninstalling" msgid="8709566347688966845">"მიმდინარეობს დეინსტალაცია…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"აღვადგინოთ <xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="INSTALLERNAME">%1$s</xliff:g>-დან?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ამ აპის ჩამოტვირთვა ფონურ რეჟიმში დაიწყება"</string> <string name="restore" msgid="8460854736328970444">"აღდგენა"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"თქვენ ხაზგარეშე რეჟიმში ხართ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ეს აპი ავტომატურად აღდგება, როცა დაუკავშირდებით ინტერნეტს"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"წარმოიქმნა შეფერხება"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ამ აპის აღდგენისას წარმოიქმნა პრობლემა"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"მეხსიერება საკმარისი არ არის"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ამ აპის აღსადგენად შეგიძლიათ, გამოათავისუფლოთ მეხსიერება ამ მოწყობილობაზე. საჭიროა მეხსიერება: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"საჭიროა მოქმედება"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ამ აპის აღსადგენად მიჰყევით შემდეგ ნაბიჯებს"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> გამორთულია"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> დეინსტალირებულია"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ამ აპის აღსადგენად უნდა დააინსტალიროთ <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"გაგრძელება"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"მეხსიერების გასუფთავება"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"პარამეტრები"</string> + <string name="close" msgid="5214897374055647996">"დახურვა"</string> </resources> diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml index 02843b8b2f3d..60f3e66e9776 100644 --- a/packages/PackageInstaller/res/values-kk/strings.xml +++ b/packages/PackageInstaller/res/values-kk/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Қолданба деректерін (<xliff:g id="SIZE">%1$s</xliff:g>) сақтау."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Осы қолданба жойылсын ба?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Осы қолданба жойылсын ба? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клоны да жойылады."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Осы құрылғыны жеке бөлмеңізден жойғыңыз келе ме?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Орындалып жатқан жою процестері"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Сәтсіз жою әрекеттері"</string> <string name="uninstalling" msgid="8709566347688966845">"Жойылуда…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> қолданбасын <xliff:g id="INSTALLERNAME">%1$s</xliff:g> арқылы қалпына келтіру керек пе?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Бұл қолданба фондық режимде жүктеп алына бастайды."</string> <string name="restore" msgid="8460854736328970444">"Қалпына келтіру"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Құрылғыңыз офлайн режимде"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Бұл қолданба құрылғыңыз интернетке қосылғанда автоматты түрде қалпына келеді."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Бірдеңе дұрыс болмады"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Бұл құрылғыны қалпына келтіру үшін әрекет жасалғанда мәселе туындады."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Жадта орын жеткіліксіз"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Бұл қолданбаны қалпына келтіргіңіз келсе, осы құрылғыда орын босатуға болады. Қажетті жад көлемі: <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Әрекет қажет"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Бұл қолданбаны қалпына келтіру үшін келесі қадамдарды орындаңыз."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> өшірілген"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> жойылған"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Бұл қолданбаны қалпына келтіру үшін орнатқышты (<xliff:g id="INSTALLERNAME">%1$s</xliff:g>) орнатуыңыз керек."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Жалғастыру"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Жадты тазалау"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Параметрлер"</string> + <string name="close" msgid="5214897374055647996">"Жабу"</string> </resources> diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml index bb849762d52f..c7a2b876b802 100644 --- a/packages/PackageInstaller/res/values-km/strings.xml +++ b/packages/PackageInstaller/res/values-km/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"រក្សាទុកទិន្នន័យកម្មវិធីទំហំ <xliff:g id="SIZE">%1$s</xliff:g>។"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"តើអ្នកចង់លុបកម្មវិធីនេះដែរឬទេ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"តើអ្នកចង់លុបកម្មវិធីនេះដែរឬទេ? ក្លូន <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ក៏នឹងត្រូវបានលុបផងដែរ។"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"តើអ្នកចង់លុបកម្មវិធីនេះចេញពី private space របស់អ្នកដែរទេ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"កំពុងដំណើរការការលុប"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ការលុបដែលបរាជ័យ"</string> <string name="uninstalling" msgid="8709566347688966845">"កំពុងលុប…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"ស្ដារ <xliff:g id="APPNAME">%1$s</xliff:g> ពី <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ឬ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"កម្មវិធីនេះនឹងចាប់ផ្ដើមទាញយកនៅផ្ទៃខាងក្រោយ"</string> <string name="restore" msgid="8460854736328970444">"ស្ដារ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"អ្នកគ្មានអ៊ីនធឺណិតទេ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"កម្មវិធីនេះនឹងស្ដារដោយស្វ័យប្រវត្តិ នៅពេលអ្នកភ្ជាប់អ៊ីនធឺណិត"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"មានអ្វីមួយខុសប្រក្រតី"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"មានបញ្ហាក្នុងការព្យាយាមស្ដារកម្មវិធីនេះ"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ទំហំផ្ទុកមិនគ្រប់គ្រាន់"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ដើម្បីស្ដារកម្មវិធីនេះ អ្នកត្រូវបង្កើនទំហំផ្ទុកទំនេរនៅលើឧបករណ៍នេះ។ តម្រូវឱ្យមានទំហំផ្ទុក៖ <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"សកម្មភាពដែលតម្រូវឱ្យធ្វើ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"អនុវត្តតាមជំហានបន្ទាប់ ដើម្បីស្ដារកម្មវិធីនេះ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ត្រូវបានបិទ"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ត្រូវបានលុប"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ដើម្បីស្ដារកម្មវិធីនេះ អ្នកនឹងត្រូវដំឡើង <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"បន្ត"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"សម្អាតទំហំផ្ទុក"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ការកំណត់"</string> + <string name="close" msgid="5214897374055647996">"បិទ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml index 476b45d57261..200edf04c01b 100644 --- a/packages/PackageInstaller/res/values-kn/strings.xml +++ b/packages/PackageInstaller/res/values-kn/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"ಆ್ಯಪ್ ಡೇಟಾದಲ್ಲಿ <xliff:g id="SIZE">%1$s</xliff:g> ಇರಿಸಿಕೊಳ್ಳಿ."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಳಿಸಲು ಬಯಸುತ್ತೀರಾ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ಕ್ಲೋನ್ ಅನ್ನು ಸಹ ಅಳಿಸಲಾಗುತ್ತದೆ."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ನಿಮ್ಮ ಖಾಸಗಿ ಸ್ಪೇಸ್ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ನೀವು ಬಯಸುತ್ತೀರಾ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ಚಾಲನೆಯಲ್ಲಿರುವ ಅನ್ಇನ್ಸ್ಟಾಲ್ಗಳು"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ವಿಫಲಗೊಂಡ ಅನ್ಇನ್ಸ್ಟಾಲ್ಗಳು"</string> <string name="uninstalling" msgid="8709566347688966845">"ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ನಿಂದ <xliff:g id="APPNAME">%1$s</xliff:g> ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಬೇಕೆ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಡೌನ್ಲೋಡ್ ಆಗಲು ಈ ಆ್ಯಪ್ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ"</string> <string name="restore" msgid="8460854736328970444">"ಮರುಸ್ಥಾಪಿಸಿ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ನೀವು ಆಫ್ಲೈನ್ನಲ್ಲಿರುವಿರಿ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ನೀವು ಇಂಟರ್ನೆಟ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿದಾಗ, ಈ ಆ್ಯಪ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮರುಸ್ಥಾಪನೆಯಾಗುತ್ತದೆ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"ಏನೋ ತಪ್ಪಾಗಿದೆ"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುವಾಗ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆ"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆ ಇಲ್ಲ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು, ನೀವು ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಥಳಾವಕಾಶವನ್ನು ತೆರವುಗೊಳಿಸಬಹುದು. ಅಗತ್ಯವಿರುವ ಸಂಗ್ರಹಣೆ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ಕ್ರಮದ ಅಗತ್ಯವಿದೆ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ಮುಂದಿನ ಹಂತಗಳನ್ನು ಅನುಸರಿಸಿ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ಅನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು, ನೀವು <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ಮುಂದುವರಿಸಿ"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ಸಂಗ್ರಹಣೆಯನ್ನು ತೆರವುಗೊಳಿಸಿ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> + <string name="close" msgid="5214897374055647996">"ಮುಚ್ಚಿರಿ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml index 7ae19ac37cce..d44909072682 100644 --- a/packages/PackageInstaller/res/values-ko/strings.xml +++ b/packages/PackageInstaller/res/values-ko/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"앱 데이터 크기를 <xliff:g id="SIZE">%1$s</xliff:g>로 유지합니다."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"이 앱을 삭제하시겠습니까?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"이 앱을 제거하시겠습니까? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 클론도 삭제됩니다."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"비공개 스페이스에서 이 앱을 제거하시겠습니까?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"실행 중인 제거 작업"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"실패한 제거 작업"</string> <string name="uninstalling" msgid="8709566347688966845">"제거 중..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>에서 <xliff:g id="APPNAME">%1$s</xliff:g> 앱을 복원하시겠습니까?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"백그라운드에서 앱이 다운로드되기 시작합니다"</string> <string name="restore" msgid="8460854736328970444">"복원"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"오프라인 상태"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"인터넷에 연결되면 이 앱은 자동으로 복원됩니다"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"문제가 발생했습니다"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"이 앱을 복원하려고 시도하던 중 문제가 발생했습니다"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"저장용량 부족"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"이 앱을 사용하려면 이 기기의 여유 공간을 확보해야 합니다 필요한 저장용량: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"조치 필요"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"이 앱을 복원하려면 다음 단계를 따르세요"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> 사용 중지됨"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> 제거됨"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"이 앱을 복원하려면 <xliff:g id="INSTALLERNAME">%1$s</xliff:g>를 설치해야 합니다"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"계속"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"저장용량 비우기"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"설정"</string> + <string name="close" msgid="5214897374055647996">"닫기"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml index faf3df24e8e4..952e6175899a 100644 --- a/packages/PackageInstaller/res/values-ky/strings.xml +++ b/packages/PackageInstaller/res/values-ky/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Колдонмонун <xliff:g id="SIZE">%1$s</xliff:g> дайындарын сактоо."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Бул колдонмону өчүрөсүзбү?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Бул колдонмону чыгарып саласызбы? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> клону да өчүрүлөт."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Бул колдонмону жеке чөйрөдөн чыгарып саласызбы?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Чыгарылып салынууда"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Чыгарылып салынбай калгандар"</string> <string name="uninstalling" msgid="8709566347688966845">"Чыгарылып салынууда…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="INSTALLERNAME">%1$s</xliff:g> платформасынан калыбына келтирилсинби?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Бул колдонмо фондо жүктөлүп алына баштайт"</string> <string name="restore" msgid="8460854736328970444">"Калыбына келтирүү"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Интернет байланышыңыз жок"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Интернетке туташканыңызда бул колдонмо автоматтык түрдө калыбына келтирилет"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Бир жерден ката кетти"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Бул колдонмону калыбына келтирүүдө маселе келип чыкты"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Сактагычта орун жетишсиз"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Бул колдонмону калыбына келтирүү үчүн түзмөктө орун бошотуңуз. Сактагыч талап кылынат: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Аракет талап кылынат"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Бул колдонмону калыбына келтирүү үчүн кийинки кадамдарды аткарыңыз"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> өчүрүлгөн"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> орнотулду"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Бул колдонмону калыбына келтирүү үчүн <xliff:g id="INSTALLERNAME">%1$s</xliff:g> кызматын орнотуп алышыңыз керек"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Улантуу"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Сактагычты тазалоо"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Параметрлер"</string> + <string name="close" msgid="5214897374055647996">"Жабуу"</string> </resources> diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml index e9b3e8e3bcbb..55c10d47dd7b 100644 --- a/packages/PackageInstaller/res/values-lo/strings.xml +++ b/packages/PackageInstaller/res/values-lo/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"ຮັກສາຂະໜາດຂໍ້ມູນແອັບ <xliff:g id="SIZE">%1$s</xliff:g> ໄວ້."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ທ່ານຕ້ອງການລຶບແອັບນີ້ບໍ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບນີ້ບໍ່? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ໂຄລນຈະຖືກລຶບນຳ."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບນີ້ຈາກພື້ນທີ່ສ່ວນຕົວຂອງທ່ານບໍ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ກຳລັງຖອນການຕິດຕັ້ງ"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ"</string> <string name="uninstalling" msgid="8709566347688966845">"ກຳລັງຖອນການຕິດຕັ້ງ..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"ກູ້ຄືນ <xliff:g id="APPNAME">%1$s</xliff:g> ຈາກ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ບໍ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ແອັບນີ້ຈະເລີ່ມດາວໂຫຼດໃນພື້ນຫຼັງ"</string> <string name="restore" msgid="8460854736328970444">"ກູ້ຄືນ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ທ່ານອອບລາຍຢູ່"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ແອັບນີ້ຈະກູ້ຄືນໂດຍອັດຕະໂນມັດເມື່ອທ່ານເຊື່ອມຕໍ່ກັບອິນເຕີເນັດ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ເກີດບັນຫາໃນລະຫວ່າງທີ່ພະຍາຍາມກູ້ຄືນແອັບນີ້"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ບ່ອນຈັດເກັບຂໍ້ມູນບໍ່ພຽງພໍ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ເພື່ອກູ້ຄືນແອັບນີ້, ທ່ານສາມາດເພີ່ມພື້ນທີ່ຫວ່າງໃນອຸປະກອນນີ້ໄດ້. ໂດຍຈະຕ້ອງໃຊ້ບ່ອນຈັດເກັບຂໍ້ມູນ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ຕ້ອງດຳເນີນການ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ປະຕິບັດຕາມຂັ້ນຕອນຕໍ່ໄປເພື່ອກູ້ຄືນແອັບນີ້"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ຖືກປິດການນຳໃຊ້"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ຖືກຖອນການຕິດຕັ້ງ"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ເພື່ອກູ້ຄືນແອັບນີ້, ທ່ານຈະຕ້ອງຕິດຕັ້ງ <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ສືບຕໍ່"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ລຶບລ້າງບ່ອນຈັດເກັບຂໍ້ມູນ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ການຕັ້ງຄ່າ"</string> + <string name="close" msgid="5214897374055647996">"ປິດ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml index 654dee06f895..1184bab48f88 100644 --- a/packages/PackageInstaller/res/values-lt/strings.xml +++ b/packages/PackageInstaller/res/values-lt/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Palikti <xliff:g id="SIZE">%1$s</xliff:g> programų duomenų."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ar norite ištrinti šią programą?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ar norite pašalinti šią programą? „<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>“ kopija taip pat bus ištrinta."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Ar norite pašalinti šią programą iš privačios erdvės?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Vykdomi pašalinimai"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nepavykę pašalinimai"</string> <string name="uninstalling" msgid="8709566347688966845">"Pašalinama…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Atkurti „<xliff:g id="APPNAME">%1$s</xliff:g>“ iš „<xliff:g id="INSTALLERNAME">%1$s</xliff:g>“?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Šios programos atsisiuntimas bus pradėtas fone"</string> <string name="restore" msgid="8460854736328970444">"Atkurti"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Esate neprisijungę"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ši programa bus automatiškai atkurta, kai prisijungsite prie interneto"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Kažkas nepavyko"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bandant atkurti šią programą iškilo problema"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Trūksta saugyklos vietos"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Kad atkurtumėte šią programą, galite atlaisvinti vietos šiame įrenginyje. Reikia saugyklos vietos: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Būtina imtis veiksmų"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Atlikite toliau nurodytus veiksmus, kad atkurtumėte šią programą"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"„<xliff:g id="INSTALLERNAME">%1$s</xliff:g>“ išjungta"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"„<xliff:g id="INSTALLERNAME">%1$s</xliff:g>“ pašalinta"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Kad atkurtumėte šią programą, turėsite įdiegti „<xliff:g id="INSTALLERNAME">%1$s</xliff:g>“"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Tęsti"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Išvalyti saugyklą"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Nustatymai"</string> + <string name="close" msgid="5214897374055647996">"Uždaryti"</string> </resources> diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml index 2d319fcc68b7..71d25d4f1cad 100644 --- a/packages/PackageInstaller/res/values-lv/strings.xml +++ b/packages/PackageInstaller/res/values-lv/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Iegūstiet <xliff:g id="SIZE">%1$s</xliff:g> (lietotnes dati)."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vai vēlaties izdzēst šo lietotni?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vai vēlaties atinstalēt šo lietotni? Tiks izdzēsts arī lietotnes <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klons."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vai vēlaties atinstalēt šo lietotni no savas privātās mapes?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Pašreizējās atinstalēšanas operācijas"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nesekmīgi atinstalēšanas mēģinājumi"</string> <string name="uninstalling" msgid="8709566347688966845">"Notiek atinstalēšana…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vai atjaunot lietotni <xliff:g id="APPNAME">%1$s</xliff:g> no <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Fonā tiks sākta šīs lietotnes lejupielāde."</string> <string name="restore" msgid="8460854736328970444">"Atjaunot"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jūs esat bezsaistē"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Šī lietotne tiks automātiski atjaunota, kad izveidosiet savienojumu ar internetu."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Radās problēma"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Mēģinot atjaunot šo lietotni, radās problēma."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Krātuvē nepietiek vietas"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Lai atjaunotu šo lietotni, jums ir jāatbrīvo vieta ierīces krātuvē. Nepieciešamā vieta: <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Nepieciešamā darbība"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Veiciet tālāk minētās darbības, lai atjaunotu šo lietotni."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Programma <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ir atspējota"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Programma <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ir atinstalēta"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Lai atjaunotu šo lietotni, jums ir jāinstalē programma <xliff:g id="INSTALLERNAME">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Turpināt"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Notīrīt krātuvi"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Iestatījumi"</string> + <string name="close" msgid="5214897374055647996">"Aizvērt"</string> </resources> diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml index e02c3c53b6db..e68fe06c10e6 100644 --- a/packages/PackageInstaller/res/values-mk/strings.xml +++ b/packages/PackageInstaller/res/values-mk/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Задржи <xliff:g id="SIZE">%1$s</xliff:g> податоци на апликацијата."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Дали сакате да ја избришете оваа апликација?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Дали сакате да ја деинсталирате оваа апликација? Ќе се избрише и клонот на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Дали сакате да ја деинсталирате апликацијава од вашиот „Приватен простор“?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Деинсталации во тек"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспешни деинсталации"</string> <string name="uninstalling" msgid="8709566347688966845">"Се деинсталира…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Да се врати <xliff:g id="APPNAME">%1$s</xliff:g> од <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Апликацијава ќе почне да се презема во заднина"</string> <string name="restore" msgid="8460854736328970444">"Враќање"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлајн сте"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Апликацијава ќе се врати автоматски кога ќе се поврзете на интернет"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Нешто тргна наопаку"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Се јави проблем при обидот за враќање на апликацијава"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Нема доволно простор"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"За да ја вратите апликацијава, може да ослободите простор на уредов. Потребен простор: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Потребно е дејство"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Следете ги следниве чекори за да ја вратите апликацијава"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Оневозможено: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Деинсталирано: <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"За да ја вратите апликацијава, треба да инсталирате <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Продолжете"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Ослободете простор"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Поставки"</string> + <string name="close" msgid="5214897374055647996">"Затворете"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml index 2e5dcfb7bb4b..0b4b81a06983 100644 --- a/packages/PackageInstaller/res/values-ml/strings.xml +++ b/packages/PackageInstaller/res/values-ml/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ആപ്പ് ഡാറ്റ വയ്ക്കുക."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ഈ ആപ്പ് ഇല്ലാതാക്കണോ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ഈ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യണോ? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ക്ലോൺ ചെയ്യലും ഇല്ലാതാക്കും."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"നിങ്ങളുടെ സ്വകാര്യ സ്പെയ്സിൽ നിന്ന് ഈ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യണോ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"നിലവിൽ അൺഇൻസ്റ്റാൾ ചെയ്യുന്നവ"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"അൺ ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയാഞ്ഞവ"</string> <string name="uninstalling" msgid="8709566347688966845">"അണ് ഇൻസ്റ്റാൾ ചെയ്യുന്നു..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="APPNAME">%1$s</xliff:g> പുനഃസ്ഥാപിക്കണോ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"പശ്ചാത്തലത്തിൽ ഈ ആപ്പ് ഡൗൺലോഡ് ചെയ്ത് തുടങ്ങും"</string> <string name="restore" msgid="8460854736328970444">"പുനഃസ്ഥാപിക്കുക"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"നിങ്ങൾ ഓഫ്ലൈനാണ്"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"നിങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ, ഈ ആപ്പ് സ്വയമേവ പുനഃസ്ഥാപിക്കും"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"എന്തോ കുഴപ്പമുണ്ടായി"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായി"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"മതിയായ സ്റ്റോറേജ് ഇല്ല"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കുന്നതിന്, ഈ ഉപകരണത്തിൽ ഇടം സൃഷ്ടിക്കാം. ആവശ്യമായ സ്റ്റോറേജ്: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"നടപടി ആവശ്യമാണ്"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കാൻ തുടർന്നുള്ള ഘട്ടങ്ങൾ പാലിക്കുക"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> അൺഇൻസ്റ്റാൾ ചെയ്തു"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കുന്നതിന്, നിങ്ങൾ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ഇൻസ്റ്റാൾ ചെയ്യേണ്ടതുണ്ട്"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"തുടരുക"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"സ്റ്റോറേജ് മായ്ക്കുക"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ക്രമീകരണം"</string> + <string name="close" msgid="5214897374055647996">"അടയ്ക്കുക"</string> </resources> diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml index 5deda94ced32..32952ae3796f 100644 --- a/packages/PackageInstaller/res/values-mn/strings.xml +++ b/packages/PackageInstaller/res/values-mn/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Аппын өгөгдлийн <xliff:g id="SIZE">%1$s</xliff:g>-г үлдээнэ үү."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Та энэ аппыг устгахыг хүсэж байна уу?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Та энэ аппыг устгахыг хүсэж байна уу? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-н хувилалыг мөн устгана."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Та энэ аппыг хувийн орон зайнаасаа устгахдаа итгэлтэй байна уу?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Устгаж байна"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Устгаж чадсангүй"</string> <string name="uninstalling" msgid="8709566347688966845">"Устгаж байна…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g>-г <xliff:g id="INSTALLERNAME">%1$s</xliff:g>-с сэргээх үү?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Энэ аппыг дэвсгэрт татаж эхэлнэ"</string> <string name="restore" msgid="8460854736328970444">"Сэргээх"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Та офлайн байна"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Таныг интернэтэд холбогдсон үед энэ аппыг автоматаар сэргээнэ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ямар нэг алдаа гарлаа"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Энэ аппыг сэргээхээр оролдоход асуудал гарлаа"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Хангалттай хадгалах сан байхгүй"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Энэ аппыг сэргээхийн тулд та энэ төхөөрөмжид сул зай гаргах боломжтой. Шаардлагатай хадгалах сан: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Арга хэмжээ авах шаардлагатай"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Энэ аппыг сэргээхийн тулд дараах алхмуудыг дагана уу"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>-г устгасан"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Энэ аппыг сэргээхийн тулд та <xliff:g id="INSTALLERNAME">%1$s</xliff:g>-г суулгах шаардлагатай"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Үргэлжлүүлэх"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Хадгалах санг цэвэрлэх"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Тохиргоо"</string> + <string name="close" msgid="5214897374055647996">"Хаах"</string> </resources> diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml index 53ca4039d93c..77a5b9b86d1e 100644 --- a/packages/PackageInstaller/res/values-mr/strings.xml +++ b/packages/PackageInstaller/res/values-mr/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"ॲप डेटा पैकी <xliff:g id="SIZE">%1$s</xliff:g> ठेवा."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"तुम्हाला हे अॅप हटवायचे आहे का?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"तुम्हाला हे अॅप अनइंस्टॉल करायचे आहे का? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोनदेखील हटवले जाईल."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"तुम्हाला तुमच्या खाजगी स्पेसमधून हे अॅप काढून टाकायचे आहे का?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"अनइंस्टॉल रन होत आहेत"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"अनइंस्टॉल करता आले नाही"</string> <string name="uninstalling" msgid="8709566347688966845">"अनइंस्टॉल करत आहे…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> मधून <xliff:g id="APPNAME">%1$s</xliff:g> रिस्टोअर करायचे आहे का?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"हे ॲप बॅकग्राउंडमध्ये डाउनलोड होण्यास सुरुवात होईल"</string> <string name="restore" msgid="8460854736328970444">"रिस्टोअर करा"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"तुम्ही ऑफलाइन आहात"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"तुम्ही इंटरनेटशी कनेक्ट केले असेल, तेव्हा हे अॅप आपोआप रिस्टोअर होईल"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"काहीतरी चुकले"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"हे अॅप रिस्टोअर करताना समस्या आली होती"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"पुरेसे स्टोरेज नाही"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"हे अॅप रिस्टोअर करण्यासाठी, तुम्ही या डिव्हाइसवरील जागा मोकळी करू शकता. स्टोरेज आवश्यक आहे: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"कृती आवश्यक आहे"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"हे अॅप रिस्टोअर करण्यासाठी पुढील पायऱ्या फॉलो करा"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> बंद केले आहे"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> अनइंस्टॉल केले आहे"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"हे अॅप रिस्टोअर करण्यासाठी, तुम्हाला <xliff:g id="INSTALLERNAME">%1$s</xliff:g> इंस्टॉल करावे लागेल"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"पुढे सुरू ठेवा"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"स्टोरेज साफ करा"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"सेटिंग्ज"</string> + <string name="close" msgid="5214897374055647996">"बंद करा"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml index 4b0b560be73d..7a8537094ef8 100644 --- a/packages/PackageInstaller/res/values-ms/strings.xml +++ b/packages/PackageInstaller/res/values-ms/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Simpan <xliff:g id="SIZE">%1$s</xliff:g> data apl."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Adakah anda mahu memadamkan apl ini?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Adakah anda mahu menyahpasang apl ini? Klon <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> juga akan dipadamkan."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Adakah anda mahu menyahpasang apl ini daripada ruang peribadi anda?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Penyahpasangan yang sedang berjalan"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Penyahpasangan yang gagal"</string> <string name="uninstalling" msgid="8709566347688966845">"Menyahpasang…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Pulihkan <xliff:g id="APPNAME">%1$s</xliff:g> daripada <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Apl ini akan mula dimuat turun dalam latar"</string> <string name="restore" msgid="8460854736328970444">"Pulihkan"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Anda berstatus luar talian"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Apl ini akan dipulihkan secara automatik apabila anda disambungkan kepada Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ada yang tidak kena"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Terdapat masalah semasa memuatkan apl ini"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Storan tidak mencukupi"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Untuk memulihkan apl ini, anda boleh mengosongkan ruang storan pada peranti ini. Storan diperlukan: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Tindakan diperlukan"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Ikut langkah seterusnya untuk memulihkan apl ini"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> dilumpuhkan"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> dinyahpasang"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Untuk memulihkan apl ini, anda perlu memasang <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Teruskan"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Kosongkan storan"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Tetapan"</string> + <string name="close" msgid="5214897374055647996">"Tutup"</string> </resources> diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml index 302c2c3a1178..21c223c12cbf 100644 --- a/packages/PackageInstaller/res/values-my/strings.xml +++ b/packages/PackageInstaller/res/values-my/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"အက်ပ်ဒေတာများ၏ <xliff:g id="SIZE">%1$s</xliff:g> ကို ထားရှိရန်။"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ဤအက်ပ်ကို ဖျက်လိုသလား။"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ဤအက်ပ်ကို ဖယ်ရှားလိုသလား။ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ပုံတူပွားကိုလည်း ဖျက်လိုက်မည်။"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ဤအက်ပ်ကို သင့်သီးသန့်နေရာမှ ဖယ်ရှားလိုသလား။"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ပရိုဂရမ်ကို ဖယ်ရှားနေပါသည်"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ပရိုဂရမ်ကို ဖယ်ရှား၍မရပါ"</string> <string name="uninstalling" msgid="8709566347688966845">"ပရိုဂရမ်ကို ဖယ်ရှားနေသည်..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> မှ <xliff:g id="APPNAME">%1$s</xliff:g> ကို ပြန်ယူမလား။"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ဤအက်ပ်ကို နောက်ခံတွင် စတင်ဒေါင်းလုဒ်လုပ်ပါမည်"</string> <string name="restore" msgid="8460854736328970444">"ပြန်ယူရန်"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"သင်အော့ဖ်လိုင်း ဖြစ်နေသည်"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"အင်တာနက် ချိတ်ဆက်ထားသည့်အခါ ဤအက်ပ်ကို အလိုအလျောက် ပြန်ယူပါမည်"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"တစ်ခုခုမှားသွားသည်"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ဤအက်ပ်ကို ပြန်ယူရန် ကြိုးပမ်းရာတွင် ပြဿနာရှိသည်"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"သိုလှောင်ခန်း မလုံလောက်ပါ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ဤအက်ပ်ကို ပြန်ယူရန် ဤစက်တွင် နေရာလွတ်ပြုလုပ်နိုင်သည်။ သိုလှောင်ခန်း လိုအပ်သည်- <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"လုပ်ဆောင်ချက် လိုအပ်သည်"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ဤအက်ပ်ကို ပြန်ယူရန် နောက်အဆင့်များအတိုင်း လုပ်ဆောင်ပါ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ကို ဖယ်ရှားထားသည်"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ဤအက်ပ်ကို ပြန်ယူရန် <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ကို ထည့်သွင်းရမည်"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ရှေ့ဆက်ရန်"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"သိုလှောင်ခန်း ရှင်းလင်းရန်"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ဆက်တင်များ"</string> + <string name="close" msgid="5214897374055647996">"ပိတ်ရန်"</string> </resources> diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml index 65c0cb800a71..86442e1249cb 100644 --- a/packages/PackageInstaller/res/values-nb/strings.xml +++ b/packages/PackageInstaller/res/values-nb/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Behold <xliff:g id="SIZE">%1$s</xliff:g> med appdata."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vil du slette denne appen?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vil du avinstallere denne appen? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-klonen slettes også."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vil du avinstallere denne appen fra det private området ditt?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Avinstalleringer som er i gang"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislykkede avinstalleringer"</string> <string name="uninstalling" msgid="8709566347688966845">"Avinstallerer …"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vil du gjenopprette <xliff:g id="APPNAME">%1$s</xliff:g> fra <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Denne appen begynner å laste ned i bakgrunnen"</string> <string name="restore" msgid="8460854736328970444">"Gjenopprett"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du er uten nett"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Denne appen gjenopprettes automatisk når du er koblet til internett"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Noe gikk galt"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Det oppsto et problem med å gjenopprette denne appen"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ikke nok lagringsplass"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"For å gjenopprette denne appen kan du frigjøre plass på enheten. Nødvendig lagringsplass: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Handling påkrevd"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Følg de neste trinnene for å gjenopprette denne appen"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> er deaktivert"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> er avinstallert"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"For å gjenopprette denne appen må du installere <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Fortsett"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Frigjør lagringsplass"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Innstillinger"</string> + <string name="close" msgid="5214897374055647996">"Lukk"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml index b72218c53af7..ee704d00c8eb 100644 --- a/packages/PackageInstaller/res/values-ne/strings.xml +++ b/packages/PackageInstaller/res/values-ne/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> एपको डेटा राख्नुहोस्।"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"तपाईं यो एप मेटाउन चाहनुहुन्छ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"तपाईं यो एप अनइन्स्टल गर्न चाहनुहुन्छ? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> क्लोन पनि मेटाइने छ।"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"तपाईं आफ्नो निजी स्पेसबाट यो एप अनइन्स्टल गर्न चाहनुहुन्छ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"चलिरहेका स्थापना रद्द गर्ने कार्यहरू"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"असफल भएका स्थापना रद्द गर्ने कार्यहरू"</string> <string name="uninstalling" msgid="8709566347688966845">"स्थापना रद्द गर्दै…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> बाट <xliff:g id="APPNAME">%1$s</xliff:g> रिस्टोर गर्ने हो?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"यो एप ब्याकग्राउन्डमा डाउनलोड हुन थाल्ने छ"</string> <string name="restore" msgid="8460854736328970444">"रिस्टोर गर्नुहोस्"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"तपाईंको डिभाइस अफलाइन छ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"तपाईंको डिभाइस इन्टरनेटमा कनेक्ट भएपछि यो एप स्वतः रिस्टोर हुने छ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"कुनै समस्या आयो"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"यो एप रिस्टोर गर्ने क्रममा कुनै समस्या आयो"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"पर्याप्त खाली ठाउँ छैन"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"यो एप रिस्टोर गर्न तपाईं यो डिभाइसमा ठाउँ खाली गर्न सक्नुहुन्छ। आवश्यक भण्डारण: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"कारबाही गर्नु पर्ने हुन्छ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"यो एप रिस्टोर गर्न आगामी चरणहरू पालना गर्नुहोस्"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> अफ गरिएको छ"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> अनइन्स्टल गरिएको छ"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"यो एप रिस्टोर गर्न तपाईंले <xliff:g id="INSTALLERNAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"जारी राख्नुहोस्"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"भण्डारण खाली गर्नुहोस्"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"सेटिङ"</string> + <string name="close" msgid="5214897374055647996">"बन्द गर्नुहोस्"</string> </resources> diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml index a1eb7083ab57..a63ff935ade9 100644 --- a/packages/PackageInstaller/res/values-nl/strings.xml +++ b/packages/PackageInstaller/res/values-nl/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> aan app-gegevens behouden."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Wil je deze app verwijderen?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Wil je deze app verwijderen? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>-kloon wordt ook verwijderd."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Wil je deze app verwijderen uit je privéruimte?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Actieve verwijderingen"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mislukte verwijderingen"</string> <string name="uninstalling" msgid="8709566347688966845">"Verwijderen…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> herstellen via <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Deze app wordt gedownload op de achtergrond"</string> <string name="restore" msgid="8460854736328970444">"Herstellen"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Je bent offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Deze app wordt automatisch hersteld als je verbinding hebt met internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Er is iets misgegaan"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Er is iets misgegaan bij het herstellen van deze app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Onvoldoende opslagruimte"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Je kunt ruimte op dit apparaat vrijmaken om deze app te herstellen. Vereiste opslagruimte: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Actie vereist"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Voer de volgende stappen uit om deze app te herstellen"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is uitgezet"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> is verwijderd"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Je moet <xliff:g id="INSTALLERNAME">%1$s</xliff:g> installeren om deze app te herstellen"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Doorgaan"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Opslag wissen"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Instellingen"</string> + <string name="close" msgid="5214897374055647996">"Sluiten"</string> </resources> diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml index a1c685c61122..c9041d1c51cc 100644 --- a/packages/PackageInstaller/res/values-or/strings.xml +++ b/packages/PackageInstaller/res/values-or/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ଆକାରର ଆପ୍ ଡାଟା ରଖନ୍ତୁୁ।"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ଆପଣ ଏହି ଆପକୁ ଡିଲିଟ କରିବାକୁ ଚାହୁଁଛନ୍ତି?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ଆପଣ ଏହି ଆପକୁ ଅନଇନଷ୍ଟଲ କରିବାକୁ ଚାହୁଁଛନ୍ତି? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> କ୍ଲୋନକୁ ମଧ୍ୟ ଡିଲିଟ କରାଯିବ।"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ଆପଣ ଆପଣଙ୍କ ପ୍ରାଇଭେଟ ସ୍ପେସରୁ ଏହି ଆପକୁ ଅନଇନଷ୍ଟଲ କରିବାକୁ ଚାହାଁନ୍ତି?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ଅନଇନଷ୍ଟଲ୍ ଚାଲୁଛି"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ବିଫଳ ହୋଇଥିବା ଅନଇନଷ୍ଟଲ୍"</string> <string name="uninstalling" msgid="8709566347688966845">"ଅନ୍ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>ରୁ <xliff:g id="APPNAME">%1$s</xliff:g>କୁ ରିଷ୍ଟୋର କରିବେ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ଏହି ଆପ ପୃଷ୍ଠପଟରେ ଡାଉନଲୋଡ ହେବା ଆରମ୍ଭ କରିବ"</string> <string name="restore" msgid="8460854736328970444">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ଆପଣ ଅଫଲାଇନ ଅଛନ୍ତି"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ଆପଣ ଇଣ୍ଟରନେଟ ସହ କନେକ୍ଟ ହେଲେ ଏହି ଆପ ସ୍ୱତଃ ରିଷ୍ଟୋର ହେବ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"କିଛି ତ୍ରୁଟି ହୋଇଛି"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ଏହି ଆପ ରିଷ୍ଟୋର କରିବାକୁ ଚେଷ୍ଟା କରିବା ସମୟରେ ଏକ ସମସ୍ୟା ହୋଇଛି"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ଯଥେଷ୍ଟ ଷ୍ଟୋରେଜ ନାହିଁ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ଏହି ଆପ ରିଷ୍ଟୋର କରିବା ପାଇଁ ଆପଣ ଏହି ଡିଭାଇସରେ ସ୍ପେସ ଖାଲି କରିପାରିବେ। ଷ୍ଟୋରେଜ ଆବଶ୍ୟକ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ପଦକ୍ଷେପ ନେବା ଆବଶ୍ୟକ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ଏହି ଆପ ରିଷ୍ଟୋର କରିବାକୁ ପରବର୍ତ୍ତୀ ଷ୍ଟେପଗୁଡ଼ିକୁ ଫଲୋ କରନ୍ତୁ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>କୁ ଅକ୍ଷମ କରାଯାଇଛି"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>କୁ ଅନଇନଷ୍ଟଲ କରାଯାଇଛି"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ଏହି ଆପ ରିଷ୍ଟୋର କରିବା ପାଇଁ ଆପଣଙ୍କୁ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ଇନଷ୍ଟଲ କରିବାକୁ ହେବ"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ଜାରି ରଖନ୍ତୁ"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ଷ୍ଟୋରେଜ ଖାଲି କରନ୍ତୁ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ସେଟିଂସ"</string> + <string name="close" msgid="5214897374055647996">"ବନ୍ଦ କରନ୍ତୁ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml index 1c7fb862f7bd..d96867231814 100644 --- a/packages/PackageInstaller/res/values-pa/strings.xml +++ b/packages/PackageInstaller/res/values-pa/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ਐਪ ਡਾਟਾ ਰੱਖੋ।"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ਦੇ ਕਲੋਨ ਨੂੰ ਵੀ ਮਿਟਾਇਆ ਜਾਵੇਗਾ।"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"ਚੱਲ ਰਹੀਆਂ ਅਣਸਥਾਪਨਾਵਾਂ"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ਅਸਫਲ ਅਣਸਥਾਪਨਾਵਾਂ"</string> <string name="uninstalling" msgid="8709566347688966845">"ਅਣਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"ਕੀ <xliff:g id="APPNAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ਤੋਂ ਮੁੜ-ਬਹਾਲ ਕਰਨਾ ਹੈ?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਊਨਲੋਡ ਹੋਣੀ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗੀ"</string> <string name="restore" msgid="8460854736328970444">"ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ਤੁਸੀਂ ਆਫ਼ਲਾਈਨ ਹੋ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ਜਦੋਂ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਇਹ ਐਪ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਮੁੜ-ਬਹਾਲ ਹੋ ਜਾਵੇਗੀ"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਵੇਲੇ ਕੋਈ ਸਮੱਸਿਆ ਆਈ"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ, ਤੁਸੀਂ ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਜਗ੍ਹਾ ਖਾਲੀ ਕਰ ਸਕਦੇ ਹੋ। ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ਕਾਰਵਾਈ ਦੀ ਲੋੜ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ ਅਗਲੇ ਪੜਾਵਾਂ ਦੀ ਪਾਲਣਾ ਕਰੋ"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ਨੂੰ ਅਣਸਥਾਪਤ ਕੀਤਾ ਗਿਆ"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ਜਾਰੀ ਰੱਖੋ"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ਸਟੋਰੇਜ ਕਲੀਅਰ ਕਰੋ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ਸੈਟਿੰਗਾਂ"</string> + <string name="close" msgid="5214897374055647996">"ਬੰਦ ਕਰੋ"</string> </resources> diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml index 8fa11ed2bcf4..6aa6aff23598 100644 --- a/packages/PackageInstaller/res/values-pl/strings.xml +++ b/packages/PackageInstaller/res/values-pl/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachowaj <xliff:g id="SIZE">%1$s</xliff:g> danych aplikacji."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Chcesz usunąć tę aplikację?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Chcesz odinstalować tę aplikację? Klon aplikacji <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> również zostanie usunięty."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Chcesz odinstalować tę aplikację ze swojego obszaru prywatnego?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktywne odinstalowania"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nieudane odinstalowania"</string> <string name="uninstalling" msgid="8709566347688966845">"Odinstalowuję…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Przywrócić aplikację <xliff:g id="APPNAME">%1$s</xliff:g> z aplikacji <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Ta aplikacja zacznie pobieranie w tle"</string> <string name="restore" msgid="8460854736328970444">"Przywróć"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jesteś offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ta aplikacja zostanie automatycznie przywrócona, gdy połączysz się z internetem"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Coś poszło nie tak"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Podczas przywracania tej aplikacji wystąpił błąd"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Za mało miejsca na dane"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Aby przywrócić tę aplikację, musisz zwolnić miejsce na urządzeniu. Wymagane miejsce na dane: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Wymagane działanie"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Aby przywrócić tę aplikację, wykonaj następne czynności"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Aplikacja <xliff:g id="INSTALLERNAME">%1$s</xliff:g> jest wyłączona"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Aplikacja <xliff:g id="INSTALLERNAME">%1$s</xliff:g> jest odinstalowana"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Aby przywrócić tę aplikację, musisz zainstalować aplikację <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Dalej"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Wyczyść pamięć"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ustawienia"</string> + <string name="close" msgid="5214897374055647996">"Zamknij"</string> </resources> diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml index 71ecb2b391fc..3f71c4964411 100644 --- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml +++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados do app."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Quer excluir este app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Quer desinstalar este app? O clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> também vai ser excluído."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Você quer desinstalar esse app do seu espaço particular?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Executando desinstalações"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Falha nas desinstalações"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restaurar <xliff:g id="APPNAME">%1$s</xliff:g> usando o app <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"O download desse app será feito em segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restaurar"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Você está off-line"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Este app será restaurado automaticamente quando você tiver uma conexão de Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo deu errado"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar restaurar este app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Armazenamento insuficiente"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para restaurar o app, libere espaço no dispositivo. Armazenamento necessário: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Ação necessária"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Siga as próximas etapas para restaurar este app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"A <xliff:g id="INSTALLERNAME">%1$s</xliff:g> está desativada"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"A <xliff:g id="INSTALLERNAME">%1$s</xliff:g> não está instalada"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para restaurar o app, instale a <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Limpar armazenamento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Configurações"</string> + <string name="close" msgid="5214897374055647996">"Fechar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml index 1d5c1a21b140..9d472767d50d 100644 --- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml +++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados da app."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Quer apagar esta app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Quer desinstalar esta app? O clone de <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> também vai ser apagado."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Quer desinstalar esta app do seu espaço privado?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalações em execução"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Desinstalações com falha"</string> <string name="uninstalling" msgid="8709566347688966845">"A desinstalar…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restaurar <xliff:g id="APPNAME">%1$s</xliff:g> a partir da <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Esta app vai começar a ser transferida em segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restaurar"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Está offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Esta app vai ser reposta automaticamente quando estabelecer ligação à Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo correu mal"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar repor esta app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Sem armazenamento suficiente"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para repor esta app, pode libertar espaço neste dispositivo. Armazenamento necessário: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Ação necessária"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Siga os passos abaixo para repor esta app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"O instalador <xliff:g id="INSTALLERNAME">%1$s</xliff:g> está desativado"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"O instalador <xliff:g id="INSTALLERNAME">%1$s</xliff:g> está desinstalado"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para repor esta app, tem de instalar o instalador <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Limpar armazenamento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Definições"</string> + <string name="close" msgid="5214897374055647996">"Fechar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml index 71ecb2b391fc..3f71c4964411 100644 --- a/packages/PackageInstaller/res/values-pt/strings.xml +++ b/packages/PackageInstaller/res/values-pt/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Manter <xliff:g id="SIZE">%1$s</xliff:g> de dados do app."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Quer excluir este app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Quer desinstalar este app? O clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> também vai ser excluído."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Você quer desinstalar esse app do seu espaço particular?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Executando desinstalações"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Falha nas desinstalações"</string> <string name="uninstalling" msgid="8709566347688966845">"Desinstalando…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restaurar <xliff:g id="APPNAME">%1$s</xliff:g> usando o app <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"O download desse app será feito em segundo plano"</string> <string name="restore" msgid="8460854736328970444">"Restaurar"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Você está off-line"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Este app será restaurado automaticamente quando você tiver uma conexão de Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo deu errado"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar restaurar este app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Armazenamento insuficiente"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para restaurar o app, libere espaço no dispositivo. Armazenamento necessário: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Ação necessária"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Siga as próximas etapas para restaurar este app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"A <xliff:g id="INSTALLERNAME">%1$s</xliff:g> está desativada"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"A <xliff:g id="INSTALLERNAME">%1$s</xliff:g> não está instalada"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para restaurar o app, instale a <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuar"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Limpar armazenamento"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Configurações"</string> + <string name="close" msgid="5214897374055647996">"Fechar"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml index 810f5449b052..a48c9c5b62c4 100644 --- a/packages/PackageInstaller/res/values-ro/strings.xml +++ b/packages/PackageInstaller/res/values-ro/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Păstrează <xliff:g id="SIZE">%1$s</xliff:g> din datele aplicației."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vrei să ștergi această aplicație?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Dezinstalezi această aplicație? Se va șterge și clona <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Dezinstalezi aplicația din spațiul privat?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Dezinstalări în curs"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Dezinstalări nereușite"</string> <string name="uninstalling" msgid="8709566347688966845">"Se dezinstalează…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Restabilești <xliff:g id="APPNAME">%1$s</xliff:g> din <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Descărcarea aplicației va începe în fundal"</string> <string name="restore" msgid="8460854736328970444">"Restabilește"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ești offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Aplicația va fi restabilită automat după ce te conectezi la internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"A apărut o eroare"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"A apărut o problemă la restabilirea aplicației"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nu există suficient spațiu de stocare"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Pentru a restabili aplicația, eliberează spațiu pe dispozitiv. Spațiu de stocare necesar: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Acțiune necesară"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Urmează pașii de mai jos pentru a restabili aplicația"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> este dezactivat"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> este dezinstalat"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Pentru a restabili aplicația, va trebui să instalezi <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Continuă"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Șterge datele stocate"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Setări"</string> + <string name="close" msgid="5214897374055647996">"Închide"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml index e368f12db0f0..93cc6d8f7d50 100644 --- a/packages/PackageInstaller/res/values-ru/strings.xml +++ b/packages/PackageInstaller/res/values-ru/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Сохранить данные приложения (<xliff:g id="SIZE">%1$s</xliff:g>)"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Удалить это приложение?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Удалить это приложение? Клон приложения <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> также будет удален."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Удалить это приложение из личного пространства?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активные процессы удаления"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ошибки удаления"</string> <string name="uninstalling" msgid="8709566347688966845">"Удаление…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Восстановить приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" из <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Приложение начнет скачиваться в фоновом режиме."</string> <string name="restore" msgid="8460854736328970444">"Восстановить"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Нет доступа к Сети"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Приложение автоматически восстановится, когда устройство будет подключено к интернету."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Что-то пошло не так"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Не удалось восстановить приложение."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Недостаточно места"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Чтобы восстановить приложение, освободите место на устройстве. Необходимо <xliff:g id="BYTES">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Требуется действие"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Чтобы восстановить приложение, следуйте дальнейшим инструкциям."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Установщик \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\" отключен"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Установщик \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\" удален"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Чтобы восстановить приложение, установите программу \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\"."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Продолжить"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Очистить хранилище"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Настройки"</string> + <string name="close" msgid="5214897374055647996">"Закрыть"</string> </resources> diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml index cea50a608624..7ee1c78fb55f 100644 --- a/packages/PackageInstaller/res/values-si/strings.xml +++ b/packages/PackageInstaller/res/values-si/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"යෙදුම් දත්තවලින් <xliff:g id="SIZE">%1$s</xliff:g> තබා ගන්න."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ඔබට මෙම යෙදුම මැකීමට අවශ්ය ද?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ඔබට මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්ය ද? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ක්ලෝනය ද මකා දැමෙනු ඇත."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"ඔබට ඔබේ පෞද්ගලික අවකාශයෙන් මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්ය ද?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"අස්ථාපන ධාවනය කරමින්"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"අසාර්ථක වූ අස්ථාපන"</string> <string name="uninstalling" msgid="8709566347688966845">"අස්ථාපනය කරමින්…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> වෙතින් <xliff:g id="APPNAME">%1$s</xliff:g> ප්රතිසාධනය කරන්න ද?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"මෙම යෙදුම පසුබිමේ බාගැනීම ආරම්භ කරනු ඇත"</string> <string name="restore" msgid="8460854736328970444">"ප්රතිසාධනය කරන්න"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ඔබ නොබැඳි වේ"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"ඔබ අන්තර්ජාලයට සම්බන්ධ වූ විට මෙම යෙදුම ස්වයංක්රීයව ප්රතිසාධනය වනු ඇත"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"යමක් වැරදී ඇත"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"මෙම යෙදුම ප්රතිසාධනය කිරීමට උත්සාහ කිරීමේ ගැටලුවක් ඇති විය"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"ප්රමාණවත් ආචයනයක් නොමැත"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"මෙම යෙදුම ප්රතිසාධනය කිරීම සඳහා, ඔබට මෙම උපාංගයෙහි ඉඩ නිදහස් කර ගත හැක. අවශ්ය ආචයනය: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ක්රියාමාර්ගය අවශ්යයි"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"මෙම යෙදුම ප්රතිසාධනය කිරීමට මීළඟ පියවර අනුගමනය කරන්න"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> අබල කර ඇත"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> අස්ථාපනය කර ඇත"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"මෙම යෙදුම ප්රතිසාධනය කිරීම සඳහා, ඔබට <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ස්ථාපනය කිරීමට අවශ්ය වනු ඇත"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ඉදිරියට යන්න"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ආචයනය හිස් කරන්න"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"සැකසීම්"</string> + <string name="close" msgid="5214897374055647996">"වසන්න"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml index 34117c364a29..785a0801ec22 100644 --- a/packages/PackageInstaller/res/values-sk/strings.xml +++ b/packages/PackageInstaller/res/values-sk/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachovať nasledujúcu veľkosť dát aplikácie: <xliff:g id="SIZE">%1$s</xliff:g>."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Chcete túto aplikáciu odstrániť?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Chcete túto aplikáciu odinštalovať? Bude odstránený aj klon aplikácie <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Chcete túto aplikáciu odinštalovať zo svojho súkromného priestoru?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Prebiehajúce odinštalovania"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neúspešné odinštalácie"</string> <string name="uninstalling" msgid="8709566347688966845">"Prebieha odinštalovanie..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Chcete obnoviť <xliff:g id="APPNAME">%1$s</xliff:g> z inštalátora <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Táto aplikácia sa začne sťahovať na pozadí"</string> <string name="restore" msgid="8460854736328970444">"Obnoviť"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ste offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Po pripojení k internetu bude táto aplikácia automaticky obnovená"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Niečo sa pokazilo"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pri pokuse o obnovenie tejto aplikácie sa vyskytol problém"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Úložisko je nedostatočné"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Ak chcete túto aplikáciu obnoviť, môžete uvoľniť priestor v tomto zariadení. Potrebný priestor: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Vyžaduje sa akcia"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Podľa nasledujúcich krokov obnovte túto aplikáciu"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Inštalátor <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je deaktivovaný"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Inštalátor <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je odinštalovaný"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Ak chcete túto aplikáciu obnoviť, musíte si nainštalovať <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Pokračovať"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Vymazať priestor"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Nastavenia"</string> + <string name="close" msgid="5214897374055647996">"Zavrieť"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml index a3037599523f..eae7c2726bc8 100644 --- a/packages/PackageInstaller/res/values-sl/strings.xml +++ b/packages/PackageInstaller/res/values-sl/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Obdrži <xliff:g id="SIZE">%1$s</xliff:g> podatkov aplikacije."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ali želite izbrisati to aplikacijo?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ali želite odmestiti to aplikacijo? Izbrisana bo tudi klonirana aplikacija <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Ali želite to aplikacijo odmestiti iz zasebnega prostora?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Odstranitve v teku"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Neuspele odstranitve"</string> <string name="uninstalling" msgid="8709566347688966845">"Odstranjevanje …"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Želite obnoviti aplikacijo <xliff:g id="APPNAME">%1$s</xliff:g> iz aplikacije <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Aplikacija bo začela prenos v ozadju"</string> <string name="restore" msgid="8460854736328970444">"Obnovi"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Nimate vzpostavljene povezave"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ta aplikacija bo samodejno obnovljena, ko boste povezani v internet."</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Prišlo je do težave"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pri obnavljanju te aplikacije je prišlo do težave."</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ni dovolj prostora za shranjevanje"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Če želite obnoviti to aplikacijo, sprostite prostor v tej napravi. Potrebni prostor za shranjevanje: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Potrebno je ukrepanje"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Če želite obnoviti to aplikacijo, uporabite postopek, opisan v nadaljevanju."</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Namestitveni program <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je onemogočen"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Namestitveni program <xliff:g id="INSTALLERNAME">%1$s</xliff:g> je odmeščen"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Če želite obnoviti to aplikacijo, morate namestiti <xliff:g id="INSTALLERNAME">%1$s</xliff:g>."</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Nadaljuj"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Počisti shrambo"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Nastavitve"</string> + <string name="close" msgid="5214897374055647996">"Zapri"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml index 7f68c8b9106e..b1f27755a220 100644 --- a/packages/PackageInstaller/res/values-sq/strings.xml +++ b/packages/PackageInstaller/res/values-sq/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Mbaj <xliff:g id="SIZE">%1$s</xliff:g> nga të dhënat e aplikacionit."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Dëshiron ta fshish këtë aplikacion?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Dëshiron ta çinstalosh këtë aplikacion? Kloni i <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> do të fshihet gjithashtu."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Dëshiron ta çinstalosh këtë aplikacion nga hapësira jote private?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Çinstalimet në ekzekutim"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Çinstalimet e dështuara"</string> <string name="uninstalling" msgid="8709566347688966845">"Po çinstalohet…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Të restaurohet <xliff:g id="APPNAME">%1$s</xliff:g> nga <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Ky aplikacion do të fillojë të shkarkohet në sfond"</string> <string name="restore" msgid="8460854736328970444">"Restauro"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Je offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ky aplikacion do të restaurohet automatikisht kur të lidhesh me internetin"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ndodhi një gabim"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pati një problem gjatë përpjekjes për të restauruar këtë aplikacion"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Hapësira ruajtëse e pamjaftueshme"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Për ta restauruar këtë aplikacion, mund të lirosh hapësirë në këtë pajisje. Hapësira ruajtëse e kërkuar: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Kërkohet veprim"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Ndiq hapat e radhës për të restauruar këtë aplikacion"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> është çaktivizuar"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> është çinstaluar"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Për ta restauruar këtë aplikacion, do të të duhet të instalosh <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Vazhdo"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Pastro hapësirën ruajtëse"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Cilësimet"</string> + <string name="close" msgid="5214897374055647996">"Mbyll"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml index 196e78473eca..3c51f3482ef3 100644 --- a/packages/PackageInstaller/res/values-sr/strings.xml +++ b/packages/PackageInstaller/res/values-sr/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Задржи <xliff:g id="SIZE">%1$s</xliff:g> података апликације."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Желите да избришете ову апликацију?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Желите да деинсталирате ову апликацију? Клон апликације <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ће такође бити избрисан."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Желите да деинсталирате ову апликацију из приватног простора?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активна деинсталирања"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспела деинсталирања"</string> <string name="uninstalling" msgid="8709566347688966845">"Деинсталира се…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Желите да вратите <xliff:g id="APPNAME">%1$s</xliff:g> из <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Апликација ће започети преузимање у позадини."</string> <string name="restore" msgid="8460854736328970444">"Врати"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлајн сте"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ова апликација ће бити враћена аутоматски када се повежете на интернет"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Дошло је до грешке"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Дошло је до проблема при враћању ове апликације"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Нема довољно меморијског простора"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Можете да ослободите простор на овом уређају да бисте вратили ову апликацију. Потребан меморијски простор: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Треба да реагујете"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Пратите даља упутства да бисте вратили ову апликацију"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Онемогућен <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Деинсталиран <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Да бисте вратили ову апликацију, треба да инсталирате <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Настави"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Обриши меморијски простор"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Подешавања"</string> + <string name="close" msgid="5214897374055647996">"Затвори"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml index 0ce696ed45f9..3fd403c884bc 100644 --- a/packages/PackageInstaller/res/values-sv/strings.xml +++ b/packages/PackageInstaller/res/values-sv/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Behåll <xliff:g id="SIZE">%1$s</xliff:g> appdata."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Vill du radera den här appen?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Vill du avinstallera den här appen? Klonen av <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> raderas också."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Vill du avinstallera appen från ditt privata rum?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Avinstallationer som pågår"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Avinstallationer som misslyckats"</string> <string name="uninstalling" msgid="8709566347688966845">"Avinstallerar …"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Vill du återställa <xliff:g id="APPNAME">%1$s</xliff:g> från <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Appen börjar ladda ned i bakgrunden"</string> <string name="restore" msgid="8460854736328970444">"Återställ"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du är offline"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Appen återställs automatiskt när du är ansluten till internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Något gick fel"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Det gick inte att återställa appen"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Inte tillräckligt med lagringsutrymme"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Frigör lagringsutrymme på enheten om du vill återställa appen. Tillgängligt lagringsutrymme som krävs: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Åtgärd krävs"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Följ anvisningarna för att återställa appen"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> är inaktiverad"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> är avinstallerad"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Du måste installera <xliff:g id="INSTALLERNAME">%1$s</xliff:g> om du vill återställa appen"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Fortsätt"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Rensa lagringsutrymme"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Inställningar"</string> + <string name="close" msgid="5214897374055647996">"Stäng"</string> </resources> diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml index 0a34d8156443..58d32777f9d5 100644 --- a/packages/PackageInstaller/res/values-sw/strings.xml +++ b/packages/PackageInstaller/res/values-sw/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Dumisha <xliff:g id="SIZE">%1$s</xliff:g> ya data ya programu."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ungependa kufuta programu hii?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ungependa kuondoa programu hii? Nakala ya <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> pia itafutwa."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Je, ungependa kuondoa programu hii kwenye nafasi yako ya faragha?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Programu zinazoondolewa"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mara ambazo programu haikuondolewa"</string> <string name="uninstalling" msgid="8709566347688966845">"Inaondoa…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Je, ungependa kurejesha <xliff:g id="APPNAME">%1$s</xliff:g> kutoka kwenye <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Itaanza kupakua programu hii chinichini"</string> <string name="restore" msgid="8460854736328970444">"Rejesha"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Uko nje ya mtandao"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Programu hii itarejeshwa kiotomatiki utakapounganisha kwenye intaneti"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Hitilafu fulani imetokea"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hitilafu fulani imetokea wakati wa kujaribu kurejesha programu hii"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nafasi ya hifadhi haitoshi"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Ili urejeshe programu hii, unaweza kufuta faili ili upate nafasi kwenye kifaa hiki. Nafasi ya hifadhi inayohitajika: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Unahitaji kuchukua hatua"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Fuata hatua zinazofuata ili urejeshe programu hii"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> imezimwa"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> imeondolewa"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Ili urejeshe programu hii, utahitaji kusakinisha <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Endelea"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Futa data kwenye hifadhi"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Mipangilio"</string> + <string name="close" msgid="5214897374055647996">"Funga"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml index 322ce5c2a3d2..4ae9e91bcf0f 100644 --- a/packages/PackageInstaller/res/values-ta/strings.xml +++ b/packages/PackageInstaller/res/values-ta/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> ஆப்ஸ் தரவை வைத்திரு."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"இந்த ஆப்ஸை நீக்க வேண்டுமா?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"இந்த ஆப்ஸை நிறுவல் நீக்க வேண்டுமா? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> குளோனும் நீக்கப்படும்."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"உங்கள் தனிப்பட்ட சேமிப்பிடத்திலிருந்து இந்த ஆப்ஸை நிறுவல் நீக்க வேண்டுமா?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"இயக்கத்திலுள்ள நிறுவல் நீக்கங்கள்"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"தோல்வியுற்ற நிறுவல் நீக்கங்கள்"</string> <string name="uninstalling" msgid="8709566347688966845">"நிறுவல் நீக்குகிறது…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>ல் இருந்து <xliff:g id="APPNAME">%1$s</xliff:g> ஐ மீட்டெடுக்கவா?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"இந்த ஆப்ஸ் பின்னணியில் பதிவிறக்கத் தொடங்கும்"</string> <string name="restore" msgid="8460854736328970444">"மீட்டெடு"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"ஆஃப்லைனில் உள்ளீர்கள்"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"நீங்கள் இணையத்துடன் இணைக்கப்பட்டிருக்கும்போது இந்த ஆப்ஸ் தானாக மீட்டெடுக்கப்படும்"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"ஏதோ தவறாகிவிட்டது"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"இந்த ஆப்ஸை மீட்டெடுக்க முயலும்போது சிக்கல் ஏற்பட்டது"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"போதுமான சேமிப்பகம் இல்லை"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"இந்த ஆப்ஸை மீட்டெடுக்க, இந்தச் சாதனத்தில் உள்ள சேமிப்பகத்தை காலியாக்கலாம். தேவைப்படும் சேமிப்பகம்: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"நடவடிக்கை தேவை"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"இந்த ஆப்ஸை மீட்டெடுக்க அடுத்த வழிமுறைகளைப் பின்பற்றுங்கள்"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> முடக்கப்பட்டுள்ளது"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> நிறுவல் நீக்கப்பட்டது"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"இந்த ஆப்ஸை மீட்டெடுக்க <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ஐ நிறுவ வேண்டும்"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"தொடர்க"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"சேமிப்பகத்தைக் காலியாக்கு"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"அமைப்புகள்"</string> + <string name="close" msgid="5214897374055647996">"மூடுக"</string> </resources> diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml index 9abc87e81de2..45f895bb3ceb 100644 --- a/packages/PackageInstaller/res/values-te/strings.xml +++ b/packages/PackageInstaller/res/values-te/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> యాప్ డేటాను ఉంచండి."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"మీరు ఈ యాప్ను తొలగించాలనుకుంటున్నారా?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"మీరు ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> క్లోన్ కూడా తొలగించబడుతుంది."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"మీరు మీ ప్రైవేట్ స్పేస్ నుండి ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"అన్ఇన్స్టాల్ చేయబడుతున్నవి"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"విఫలమైన అన్ఇన్స్టాల్లు"</string> <string name="uninstalling" msgid="8709566347688966845">"అన్ఇన్స్టాల్ చేస్తోంది…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> నుండి <xliff:g id="APPNAME">%1$s</xliff:g>ను రీస్టోర్ చేయాలా?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"ఈ యాప్ బ్యాక్గ్రౌండ్లో డౌన్లోడ్ అవ్వడం ప్రారంభమవుతుంది"</string> <string name="restore" msgid="8460854736328970444">"రీస్టోర్ చేయండి"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"మీరు ఆఫ్లైన్లో ఉన్నారు"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"మీరు ఇంటర్నెట్కు కనెక్ట్ అయ్యి ఉన్నప్పుడు ఈ యాప్ ఆటోమేటిక్గా రీస్టోర్ చేయబడుతుంది"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"ఏదో పొరపాటు జరిగింది"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"ఈ యాప్ను రీస్టోర్ చేయడానికి ట్రై చేస్తున్నపుడు సమస్య ఏర్పడింది"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"తగినంత స్టోరేజ్ స్పేస్ లేదు"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"ఈ యాప్ను రీస్టోర్ చేయడానికి, మీరు ఈ పరికరంలో స్పేస్ను ఖాళీ చేయవచ్చు. స్టోరేజ్ అవసరం: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"మీరు పూర్తి చేయాల్సిన చర్య"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ఈ యాప్ను రీస్టోర్ చేయడానికి తదుపరి దశలను ఫాలో అవ్వండి"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> డిజేబుల్ చేయబడింది"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> అన్ఇన్స్టాల్ చేయబడింది"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"ఈ యాప్ను రీస్టోర్ చేయడానికి, మీరు <xliff:g id="INSTALLERNAME">%1$s</xliff:g>ను ఇన్స్టాల్ చేయాలి"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"కొనసాగించండి"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"స్టోరేజ్ను క్లియర్ చేయండి"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"సెట్టింగ్లు"</string> + <string name="close" msgid="5214897374055647996">"మూసివేయండి"</string> </resources> diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml index b1faa1c35d67..7cdb3b29ce67 100644 --- a/packages/PackageInstaller/res/values-th/strings.xml +++ b/packages/PackageInstaller/res/values-th/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"เก็บข้อมูลแอปไว้ <xliff:g id="SIZE">%1$s</xliff:g>"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"ต้องการลบแอปนี้ไหม"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"ต้องการถอนการติดตั้งแอปนี้ไหม ระบบจะลบโคลนของ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ด้วย"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"คุณต้องการถอนการติดตั้งแอปนี้จากพื้นที่ส่วนตัวไหม"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"กำลังเรียกใช้การถอนการติดตั้ง"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ถอนการติดตั้งไม่สำเร็จ"</string> <string name="uninstalling" msgid="8709566347688966845">"กำลังถอนการติดตั้ง…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"กู้คืน <xliff:g id="APPNAME">%1$s</xliff:g> จาก <xliff:g id="INSTALLERNAME">%1$s</xliff:g> ใช่ไหม"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"แอปนี้จะเริ่มดาวน์โหลดในเบื้องหลัง"</string> <string name="restore" msgid="8460854736328970444">"กู้คืน"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"คุณออฟไลน์อยู่"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"แอปนี้จะคืนค่าโดยอัตโนมัติเมื่อคุณเชื่อมต่อกับอินเทอร์เน็ต"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"เกิดข้อผิดพลาด"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"เกิดปัญหาขณะพยายามคืนค่าแอปนี้"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"พื้นที่เก็บข้อมูลไม่เพียงพอ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"หากต้องการคืนค่าแอปนี้ คุณสามารถเพิ่มพื้นที่ว่างในอุปกรณ์ โดยจะต้องใช้พื้นที่เก็บข้อมูล <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"ขอให้ดำเนินการ"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"ทำตามขั้นตอนถัดไปเพื่อคืนค่าแอปนี้"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ถูกปิดใช้"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ถูกถอนการติดตั้ง"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"หากต้องการคืนค่าแอปนี้ คุณจะต้องติดตั้ง <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"ต่อไป"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"ล้างพื้นที่เก็บข้อมูล"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"การตั้งค่า"</string> + <string name="close" msgid="5214897374055647996">"ปิด"</string> </resources> diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml index 8e6e2517e473..f08403073421 100644 --- a/packages/PackageInstaller/res/values-tl/strings.xml +++ b/packages/PackageInstaller/res/values-tl/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Panatilihin ang <xliff:g id="SIZE">%1$s</xliff:g> ng data ng app."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Gusto mo bang i-delete ang app na ito?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Gusto mo bang i-uninstall ang app na ito? Made-delete din ang clone ng <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Gusto mo bang i-uninstall ang app na ito sa iyong pribadong space?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Mga kasalukuyang pag-uninstall"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Mga hindi na-uninstall"</string> <string name="uninstalling" msgid="8709566347688966845">"Ina-uninstall…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"I-restore ang <xliff:g id="APPNAME">%1$s</xliff:g> mula sa <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Magsisimulang mag-download ang app na ito sa background"</string> <string name="restore" msgid="8460854736328970444">"I-restore"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Offline ka"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Awtomatikong mare-restore ang app na ito kapag nakakonekta ka sa internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nagkaproblema"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Nagkaproblema sa pagsubok na i-restore ang app na ito"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Hindi sapat ang storage"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Para i-restore ang app na ito, puwede kang magbakante ng espasyo sa device na ito. Kinakailangang storage: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Nangangailangan ng pagkilos"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Sundin ang mga susunod na hakbang para i-restore ang app na ito"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Naka-disable ang <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Na-uninstall ang <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Para i-restore ang app na ito, kailangan mong i-install ang <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Magpatuloy"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"I-clear ang storage"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Mga Setting"</string> + <string name="close" msgid="5214897374055647996">"Isara"</string> </resources> diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml index dc840306e091..c3229910b0eb 100644 --- a/packages/PackageInstaller/res/values-tr/strings.xml +++ b/packages/PackageInstaller/res/values-tr/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Uygulama verilerinin <xliff:g id="SIZE">%1$s</xliff:g> kadarını sakla."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Bu uygulamayı silmek istiyor musunuz?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> klonu da silinecektir."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Bu uygulamanın gizli alanınızdaki yüklemesini kaldırmak istiyor musunuz?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Devam eden yükleme kaldırma işlemleri"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Başarısız yükleme kaldırma işlemleri"</string> <string name="uninstalling" msgid="8709566347688966845">"Yükleme kaldırılıyor…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> uygulaması <xliff:g id="INSTALLERNAME">%1$s</xliff:g> üzerinden geri yüklensin mi?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Bu uygulama arka planda indirilmeye başlanacak"</string> <string name="restore" msgid="8460854736328970444">"Geri yükle"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"İnternete bağlı değilsiniz"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Bu uygulama, internete bağlandığınızda otomatik olarak geri yüklenecek"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Bir hata oluştu"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu uygulamayı geri yüklemeye çalışırken bir sorun oluştu"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Yeterli depolama alanı yok"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Bu uygulamayı geri yüklemek için bu cihazda yer açabilirsiniz. Bunun için <xliff:g id="BYTES">%1$s</xliff:g> depolama alanı gerekiyor"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"İşlem gerekli"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Bu uygulamayı geri yüklemek için sonraki adımları uygulayın"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> devre dışı bırakıldı"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> yüklemesi kaldırılmış"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Bu uygulamayı geri yüklemek için <xliff:g id="INSTALLERNAME">%1$s</xliff:g> yükleyicisini yüklemeniz gerekiyor"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Devam"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Depolama alanını temizle"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Ayarlar"</string> + <string name="close" msgid="5214897374055647996">"Kapat"</string> </resources> diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml index 3d7083f9f1f8..657eb3914c5f 100644 --- a/packages/PackageInstaller/res/values-uk/strings.xml +++ b/packages/PackageInstaller/res/values-uk/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Залишити <xliff:g id="SIZE">%1$s</xliff:g> даних додатка."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Видалити цей додаток?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Видалити цей додаток? Копію <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> також буде видалено."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Видалити цей додаток із вашого приватного простору?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активні видалення"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Невиконані видалення"</string> <string name="uninstalling" msgid="8709566347688966845">"Видалення..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Відновити додаток <xliff:g id="APPNAME">%1$s</xliff:g> з <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Цей додаток почне завантажуватись у фоновому режимі"</string> <string name="restore" msgid="8460854736328970444">"Відновити"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Пристрій не в мережі"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Додаток автоматично відновиться, коли пристрій підключиться до Інтернету"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Помилка"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Під час спроби відновити цей додаток сталася помилка"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Недостатньо пам’яті"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Щоб відновити цей додаток, ви можете звільнити місце на пристрої. Потрібний обсяг пам’яті: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Потрібна дія"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Виконайте подальші вказівки, щоб відновити цей додаток"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> вимкнено"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> видалено"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Щоб відновити цей додаток, установіть <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Продовжити"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Очистити пам’ять"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Налаштування"</string> + <string name="close" msgid="5214897374055647996">"Закрити"</string> </resources> diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml index 08a8315d5caf..f496e1022e51 100644 --- a/packages/PackageInstaller/res/values-ur/strings.xml +++ b/packages/PackageInstaller/res/values-ur/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"ایپ ڈیٹا کا <xliff:g id="SIZE">%1$s</xliff:g> رکھیں۔"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"کیا آپ یہ ایپ حذف کرنا چاہتے ہیں؟"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"کیا آپ یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟ <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> کلون کو بھی حذف کر دیا جائے گا۔"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"کیا آپ اس ایپ کو اپنی نجی جگہ سے اَن انسٹال کرنا چاہتے ہیں؟"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"چل رہے اَن انسٹالز"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"ناکام اَن انسٹالز"</string> <string name="uninstalling" msgid="8709566347688966845">"اَن انسٹال ہو رہا ہے…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> کو <xliff:g id="INSTALLERNAME">%1$s</xliff:g> سے بحال کریں"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"یہ ایپ پس منظر میں ڈاؤن لوڈ ہونا شروع ہو جائے گی"</string> <string name="restore" msgid="8460854736328970444">"بحال کریں"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"آپ آف لائن ہیں"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"جب آپ انٹرنیٹ سے منسلک ہوں گے تو یہ ایپ خودکار طور پر بحال ہو جائے گی"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"کچھ غلط ہو گیا"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"اس ایپ کو بحال کرنے کی کوشش میں ایک مسئلہ تھا"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"کافی اسٹوریج نہیں ہے"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"اس ایپ کو بحال کرنے کے لیے، آپ اس آلہ پر اسپیس خالی کر سکتے ہیں۔ درکار اسٹوریج: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"کارروائی درکار ہے"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"اس ایپ کو بحال کرنے کے لیے اگلے مراحل کی پیروی کریں"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> غیر فعال ہے"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> اَن انسٹال ہو گیا ہے"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"اس ایپ کو بحال کرنے کے لیے، آپ کو <xliff:g id="INSTALLERNAME">%1$s</xliff:g> انسٹال کرنا ہوگا"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"جاری رکھیں"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"اسٹوریج صاف کریں"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"ترتیبات"</string> + <string name="close" msgid="5214897374055647996">"بند کریں"</string> </resources> diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml index 894e8676f8d4..a1f116d9e08f 100644 --- a/packages/PackageInstaller/res/values-uz/strings.xml +++ b/packages/PackageInstaller/res/values-uz/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"<xliff:g id="SIZE">%1$s</xliff:g> hajmdagi ilova axborotlari saqlab qolinsin"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Bu ilova oʻchirilsinmi?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Bu ilovani oʻchirib tashlamoqchimisiz? <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nusxasi ham oʻchib ketadi."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Bu ilovani maxfiy joydan olib tashlamoqchimisiz?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Davom etayotganlar"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Amalga oshmaganlar"</string> <string name="uninstalling" msgid="8709566347688966845">"O‘chirilmoqda…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"<xliff:g id="APPNAME">%1$s</xliff:g> ilovasi <xliff:g id="INSTALLERNAME">%1$s</xliff:g> orqali tiklansinmi?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Bu ilova orqa fonda yuklab olinishi boshlanadi"</string> <string name="restore" msgid="8460854736328970444">"Tiklash"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Siz internetga ulanmagansiz"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Internetga ulanganingizda bu ilova avtomatik ravishda tiklanadi"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nimadir xato ketdi"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu ilovani tiklashda muammo chiqdi"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Xotirada yetarli boʻsh joy yoʻq"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Bu ilovani tiklash uchun ushbu qurilmada joy ochishingiz mumkin. Kerakli joy: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Amal bajarish zarur"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Bu ilovani tiklash uchun keyingi qadamlarga rioya qiling"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> faolsizlantirilgan"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> oʻchirib tashlandi"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Bu ilovani tiklash uchun <xliff:g id="INSTALLERNAME">%1$s</xliff:g> oʻrnatilishi kerak"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Davom etish"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Xotirani tozalash"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Sozlamalar"</string> + <string name="close" msgid="5214897374055647996">"Yopish"</string> </resources> diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml index 14959da4d147..39d67251e469 100644 --- a/packages/PackageInstaller/res/values-vi/strings.xml +++ b/packages/PackageInstaller/res/values-vi/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Giữ lại <xliff:g id="SIZE">%1$s</xliff:g> dữ liệu ứng dụng."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Bạn có muốn xoá ứng dụng này không?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Bạn có muốn gỡ cài đặt ứng dụng này không? Bản sao của <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> cũng sẽ bị xoá."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Bạn có muốn gỡ cài đặt ứng dụng này khỏi không gian riêng tư của mình không?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Các quá trình gỡ cài đặt đang chạy"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Gỡ cài đặt không thành công"</string> <string name="uninstalling" msgid="8709566347688966845">"Đang gỡ cài đặt..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Khôi phục <xliff:g id="APPNAME">%1$s</xliff:g> từ <xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Ứng dụng này sẽ bắt đầu tải xuống ở chế độ nền"</string> <string name="restore" msgid="8460854736328970444">"Khôi phục"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Không có kết nối mạng"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Ứng dụng này sẽ tự động khôi phục khi bạn kết nối với Internet"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Đã xảy ra lỗi"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Đã xảy ra vấn đề khi tìm cách khôi phục ứng dụng này"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Không đủ dung lượng lưu trữ"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Để khôi phục ứng dụng này, bạn có thể giải phóng dung lượng trên thiết bị này. Dung lượng lưu trữ cần thiết: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Việc cần làm"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Hãy làm theo các bước tiếp theo để khôi phục ứng dụng này"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> đã bị tắt"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> đã bị gỡ cài đặt"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Để khôi phục ứng dụng này, bạn sẽ phải cài đặt <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Tiếp tục"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Xoá bộ nhớ"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Cài đặt"</string> + <string name="close" msgid="5214897374055647996">"Đóng"</string> </resources> diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml index ae125593ab6e..07cadbfb4ad4 100644 --- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml +++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"保留 <xliff:g id="SIZE">%1$s</xliff:g> 的应用数据。"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"要删除此应用吗?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"要卸载此应用吗?<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> 克隆应用也会被删除。"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"要从私密空间卸载此应用吗?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"正在进行卸载操作"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"卸载操作失败"</string> <string name="uninstalling" msgid="8709566347688966845">"正在卸载…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"要从 <xliff:g id="INSTALLERNAME">%1$s</xliff:g> 中恢复 <xliff:g id="APPNAME">%1$s</xliff:g> 吗?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"系统将开始在后台下载此应用"</string> <string name="restore" msgid="8460854736328970444">"恢复"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"您没有联网"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"当您联网后,此应用将自动恢复"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"出了点问题"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"尝试恢复此应用时出现问题"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"存储空间不足"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"若要恢复此应用,您需要释放此设备上的空间。所需的存储空间为:<xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"请采取行动"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"请按照后续步骤恢复此应用"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> 已停用"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> 已卸载"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"若要恢复此应用,您需要安装 <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"继续"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"清空存储空间"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"设置"</string> + <string name="close" msgid="5214897374055647996">"关闭"</string> </resources> diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml index 5a79817b7e6e..3e13aff12e27 100644 --- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml +++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"保留應用程式資料 (<xliff:g id="SIZE">%1$s</xliff:g>)。"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"要刪除此應用程式嗎?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"要解除安裝此應用程式嗎?「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」複製本亦將被刪除。"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"要從私人空間解除安裝此應用程式嗎?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"正在執行的解除安裝操作"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"失敗的解除安裝操作"</string> <string name="uninstalling" msgid="8709566347688966845">"正在解除安裝…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"要從「<xliff:g id="INSTALLERNAME">%1$s</xliff:g>」還原「<xliff:g id="APPNAME">%1$s</xliff:g>」嗎?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"此應用程式將開始在背景下載"</string> <string name="restore" msgid="8460854736328970444">"還原"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"你已離線"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"連接互聯網後,系統就會自動還原此應用程式"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"發生問題"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"嘗試還原此應用程式時發生問題"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"儲存空間不足"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"如要還原此應用程式,請騰出此裝置的儲存空間。需要的儲存空間大小:<xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"請即行動"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"按照後續步驟還原此應用程式"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"已停用 <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"已解除安裝 <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"如要還原此應用程式,你必須安裝 <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"繼續"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"清除儲存空間"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"設定"</string> + <string name="close" msgid="5214897374055647996">"關閉"</string> </resources> diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml index 873392bf240c..f457462196b5 100644 --- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml +++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"保留 <xliff:g id="SIZE">%1$s</xliff:g> 的應用程式資料。"</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"要刪除這個應用程式嗎?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"要解除安裝這個應用程式嗎?系統也會一併刪除「<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>」副本。"</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"想要從私人空間解除安裝這個應用程式嗎?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"執行中的解除安裝作業"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"失敗的解除安裝作業"</string> <string name="uninstalling" msgid="8709566347688966845">"解除安裝中…"</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"要從「<xliff:g id="INSTALLERNAME">%1$s</xliff:g>」還原「<xliff:g id="APPNAME">%1$s</xliff:g>」嗎?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"系統將開始在背景下載這個應用程式"</string> <string name="restore" msgid="8460854736328970444">"還原"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"裝置目前離線"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"連上網際網路後,系統就會自動還原這個應用程式"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"發生錯誤"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"嘗試還原這個應用程式時發生問題"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"儲存空間不足"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"如要還原這個應用程式,請釋出這部裝置的儲存空間。需要的儲存空間大小:<xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"敬請採取行動"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"請按照後續步驟還原這個應用程式"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"「<xliff:g id="INSTALLERNAME">%1$s</xliff:g>」已停用"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"「<xliff:g id="INSTALLERNAME">%1$s</xliff:g>」已解除安裝"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"如要還原這個應用程式,請安裝「<xliff:g id="INSTALLERNAME">%1$s</xliff:g>」"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"繼續"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"清除儲存空間"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"設定"</string> + <string name="close" msgid="5214897374055647996">"關閉"</string> </resources> diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml index 8ff1e38bc7a3..1de22a47ab59 100644 --- a/packages/PackageInstaller/res/values-zu/strings.xml +++ b/packages/PackageInstaller/res/values-zu/strings.xml @@ -66,6 +66,7 @@ <string name="uninstall_keep_data" msgid="7002379587465487550">"Gcina u-<xliff:g id="SIZE">%1$s</xliff:g> wedatha yohlelo lokusebenza."</string> <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Ingabe ufuna ukusula le app?"</string> <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Ufuna ukukhipha le app? I-clone ye-<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> nayo izosulwa."</string> + <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Ingabe ufuna ukukhipha le app endaweni yakho egodliwe?"</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Ukukhishwa okuqhubekayo"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Ukukhishwa okuhlulekile"</string> <string name="uninstalling" msgid="8709566347688966845">"Iyakhipha..."</string> @@ -106,4 +107,21 @@ <string name="unarchive_application_title" msgid="7958278328280721421">"Buyisela i-<xliff:g id="APPNAME">%1$s</xliff:g> ukusuka ku-<xliff:g id="INSTALLERNAME">%1$s</xliff:g>?"</string> <string name="unarchive_body_text" msgid="8244155079861708964">"Le app izoqala ukudawuniloda ingemuva"</string> <string name="restore" msgid="8460854736328970444">"Buyisela"</string> + <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Awuxhunyiwe ku-inthanethi"</string> + <string name="unarchive_error_offline_body" msgid="255717000519772755">"Le app izobuyiswa ngokuzenzakalelayo uma usuxhume ku-inthanethi"</string> + <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Kukhona okungahambanga kahle"</string> + <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Kube nenkinga ekuzameni ukubuyisa le app"</string> + <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Indawo yokubeka ayanele"</string> + <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Ukuze ubuyise le app, ungavula isikhala kule divayisi. Isitoreji esidingekayo: <xliff:g id="BYTES">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_title" msgid="4971245740162604619">"Kudingeka isenzo"</string> + <string name="unarchive_action_required_body" msgid="1679431572983989231">"Landela izinyathelo ezilandelayo ukuze ubuyise le app"</string> + <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"<xliff:g id="INSTALLERNAME">%1$s</xliff:g> ivimbelwe"</string> + <!-- no translation found for unarchive_error_installer_disabled_body (4820821285907011729) --> + <skip /> + <string name="unarchive_error_installer_uninstalled_title" msgid="3748354109176326489">"Okuthi <xliff:g id="INSTALLERNAME">%1$s</xliff:g> kukhishiwe"</string> + <string name="unarchive_error_installer_uninstalled_body" msgid="944733542444183204">"Ukuze ubuyise le app, kuzodingeka ufake okuthi <xliff:g id="INSTALLERNAME">%1$s</xliff:g>"</string> + <string name="unarchive_action_required_continue" msgid="5711202111224184257">"Qhubeka"</string> + <string name="unarchive_clear_storage_button" msgid="1549537154535608744">"Sula isitoreji"</string> + <string name="unarchive_settings_button" msgid="3504171760009177425">"Amasethingi"</string> + <string name="close" msgid="5214897374055647996">"Vala"</string> </resources> diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index 60bf48c8b75e..8b136da04405 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath allprojects { extra["androidTop"] = androidTop - extra["jetpackComposeVersion"] = "1.6.0-beta01" + extra["jetpackComposeVersion"] = "1.6.0-beta02" } subprojects { diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt index 90c7d46c3004..f4edb36b5e76 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt @@ -48,6 +48,7 @@ import com.android.settingslib.spa.gallery.preference.PreferencePageProvider import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider +import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider import com.android.settingslib.spa.gallery.ui.CategoryPageProvider import com.android.settingslib.spa.gallery.ui.CopyablePageProvider import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider @@ -100,6 +101,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) { SettingsExposedDropdownMenuCheckBoxProvider, SettingsTextFieldPasswordPageProvider, SearchScaffoldPageProvider, + SuwScaffoldPageProvider, CardPageProvider, CopyablePageProvider, ), diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt index 1d897f77011e..6a2e5985a735 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt @@ -43,6 +43,7 @@ import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider import com.android.settingslib.spa.gallery.page.SliderPageProvider import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider +import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider import com.android.settingslib.spa.gallery.ui.CategoryPageProvider import com.android.settingslib.spa.gallery.ui.CopyablePageProvider import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider @@ -59,6 +60,7 @@ object HomePageProvider : SettingsPageProvider { OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(), SearchScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), + SuwScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(), diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt new file mode 100644 index 000000000000..6fc8de3ac49c --- /dev/null +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.gallery.scaffold + +import android.os.Bundle +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.SignalCellularAlt +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.android.settingslib.spa.framework.common.SettingsEntryBuilder +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.common.createSettingsPage +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.gallery.R +import com.android.settingslib.spa.widget.illustration.Illustration +import com.android.settingslib.spa.widget.illustration.IllustrationModel +import com.android.settingslib.spa.widget.illustration.ResourceType +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton +import com.android.settingslib.spa.widget.scaffold.SuwScaffold +import com.android.settingslib.spa.widget.ui.SettingsBody + +private const val TITLE = "Sample SuwScaffold" + +object SuwScaffoldPageProvider : SettingsPageProvider { + override val name = "SuwScaffold" + + private val owner = createSettingsPage() + + fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner) + .setUiLayoutFn { + Preference(object : PreferenceModel { + override val title = TITLE + override val onClick = navigator(name) + }) + } + + @Composable + override fun Page(arguments: Bundle?) { + Page() + } +} + +@Composable +private fun Page() { + SuwScaffold( + imageVector = Icons.Outlined.SignalCellularAlt, + title = "Connect to mobile network", + actionButton = BottomAppBarButton("Next") {}, + dismissButton = BottomAppBarButton("Cancel") {}, + ) { + Column(Modifier.padding(SettingsDimension.itemPadding)) { + SettingsBody("To add another SIM, download a new eSIM.") + } + Illustration(object : IllustrationModel { + override val resId = R.drawable.accessibility_captioning_banner + override val resourceType = ResourceType.IMAGE + }) + Column(Modifier.padding(SettingsDimension.itemPadding)) { + SettingsBody("To add another SIM, download a new eSIM.") + } + Illustration(object : IllustrationModel { + override val resId = R.drawable.accessibility_captioning_banner + override val resourceType = ResourceType.IMAGE + }) + } +} diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index acd90f3c4f4d..7eccfe5ed508 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { api("androidx.slice:slice-builders:1.1.0-alpha02") api("androidx.slice:slice-core:1.1.0-alpha02") api("androidx.slice:slice-view:1.1.0-alpha02") - api("androidx.compose.material3:material3:1.2.0-alpha11") + api("androidx.compose.material3:material3:1.2.0-alpha12") api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index 5a1120e6c7a1..c143390f269c 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -37,11 +37,13 @@ object SettingsDimension { val itemPaddingAround = 8.dp val itemDividerHeight = 32.dp + val iconLarge = 48.dp + /** The size when app icon is displayed in list. */ val appIconItemSize = 32.dp /** The size when app icon is displayed in App info page. */ - val appIconInfoSize = 48.dp + val appIconInfoSize = iconLarge /** The vertical padding for buttons. */ val buttonPaddingVertical = 12.dp diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt index e761a33cf67a..caceb6f6ec4a 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/BarChart.kt @@ -38,6 +38,8 @@ import androidx.compose.ui.viewinterop.AndroidView import com.android.settingslib.spa.framework.theme.divider import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.components.YAxis.AxisDependency import com.github.mikephil.charting.data.BarData import com.github.mikephil.charting.data.BarDataSet import com.github.mikephil.charting.data.BarEntry @@ -90,6 +92,10 @@ interface BarChartModel { /** If set to true, touch gestures are enabled on the [BarChart]. */ val enableBarchartTouch: Boolean get() = true + + /** The renderer provider for x-axis. */ + val xAxisRendererProvider: XAxisRendererProvider? + get() = null } data class BarChartData( @@ -143,6 +149,16 @@ fun BarChart(barChartModel: BarChartModel) { yOffset = 10f } + barChartModel.xAxisRendererProvider?.let { + setXAxisRenderer( + it.provideXAxisRenderer( + getViewPortHandler(), + getXAxis(), + getTransformer(YAxis.AxisDependency.LEFT) + ) + ) + } + axisLeft.apply { axisMaximum = barChartModel.yAxisMaxValue axisMinimum = barChartModel.yAxisMinValue diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.kt new file mode 100644 index 000000000000..6569d25dd00d --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/chart/XAxisRendererProvider.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.settingslib.spa.widget.chart + +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.renderer.XAxisRenderer +import com.github.mikephil.charting.utils.Transformer +import com.github.mikephil.charting.utils.ViewPortHandler + +/** A provider for [XAxisRenderer] objects. */ +fun interface XAxisRendererProvider { + + /** Provides an object of [XAxisRenderer] type. */ + fun provideXAxisRenderer( + viewPortHandler: ViewPortHandler, + xAxis: XAxis, + transformer: Transformer + ): XAxisRenderer +}
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt index 3819a1075adb..e0dd4e17ce38 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt @@ -34,6 +34,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme fun SettingsOutlinedTextField( value: String, label: String, + errorMessage: String? = null, singleLine: Boolean = true, enabled: Boolean = true, onTextChange: (String) -> Unit, @@ -49,6 +50,12 @@ fun SettingsOutlinedTextField( }, singleLine = singleLine, enabled = enabled, + isError = errorMessage != null, + supportingText = { + if (errorMessage != null) { + Text(text = errorMessage) + } + } ) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt new file mode 100644 index 000000000000..354b95ddcbfe --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.scaffold + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.toMediumWeight + +data class BottomAppBarButton( + val text: String, + val onClick: () -> Unit, +) + +@Composable +fun SuwScaffold( + imageVector: ImageVector, + title: String, + actionButton: BottomAppBarButton? = null, + dismissButton: BottomAppBarButton? = null, + content: @Composable ColumnScope.() -> Unit, +) { + ActivityTitle(title) + Scaffold { innerPadding -> + BoxWithConstraints( + Modifier + .padding(innerPadding) + .padding(top = SettingsDimension.itemPaddingAround) + ) { + // Use single column layout in portrait, two columns in landscape. + val useSingleColumn = maxWidth < maxHeight + if (useSingleColumn) { + Column { + Column( + Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) + ) { + Header(imageVector, title) + content() + } + BottomBar(actionButton, dismissButton) + } + } else { + Column(Modifier.padding(horizontal = SettingsDimension.itemPaddingAround)) { + Row((Modifier.weight(1f))) { + Box(Modifier.weight(1f)) { + Header(imageVector, title) + } + Column( + Modifier + .weight(1f) + .verticalScroll(rememberScrollState())) { + content() + } + } + BottomBar(actionButton, dismissButton) + } + } + } + } +} + +@Composable +private fun Header( + imageVector: ImageVector, + title: String +) { + Column(Modifier.padding(SettingsDimension.itemPadding)) { + Icon( + imageVector = imageVector, + contentDescription = null, + modifier = Modifier + .padding(vertical = SettingsDimension.itemPaddingAround) + .size(SettingsDimension.iconLarge), + tint = MaterialTheme.colorScheme.primary, + ) + Text( + text = title, + modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingVertical), + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.displaySmall, + ) + } +} + +@Composable +private fun BottomBar( + actionButton: BottomAppBarButton?, + dismissButton: BottomAppBarButton?, +) { + Row(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) { + dismissButton?.apply { + TextButton(onClick) { + ActionText(text) + } + } + Spacer(modifier = Modifier.weight(1f)) + actionButton?.apply { + Button(onClick) { + ActionText(text) + } + } + } +} + +@Composable +private fun ActionText(text: String) { + Text( + text = text, + style = MaterialTheme.typography.bodyMedium.toMediumWeight(), + ) +} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt new file mode 100644 index 000000000000..35c9f78ada68 --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SuwScaffoldTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.scaffold + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.SignalCellularAlt +import androidx.compose.material3.Text +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SuwScaffoldTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun suwScaffold_titleIsDisplayed() { + composeTestRule.setContent { + SuwScaffold(imageVector = Icons.Outlined.SignalCellularAlt, title = TITLE) { + Text(text = "AAA") + Text(text = "BBB") + } + } + + composeTestRule.onNodeWithText(TITLE).assertIsDisplayed() + } + + @Test + fun suwScaffold_itemsAreDisplayed() { + composeTestRule.setContent { + SuwScaffold(imageVector = Icons.Outlined.SignalCellularAlt, title = TITLE) { + Text(text = "AAA") + Text(text = "BBB") + } + } + + composeTestRule.onNodeWithText("AAA").assertIsDisplayed() + composeTestRule.onNodeWithText("BBB").assertIsDisplayed() + } + + @Test + fun suwScaffold_actionButtonDisplayed() { + composeTestRule.setContent { + SuwScaffold( + imageVector = Icons.Outlined.SignalCellularAlt, + title = TITLE, + actionButton = BottomAppBarButton(TEXT) {}, + ) {} + } + + composeTestRule.onNodeWithText(TEXT).assertIsDisplayed() + } + + @Test + fun suwScaffold_dismissButtonDisplayed() { + composeTestRule.setContent { + SuwScaffold( + imageVector = Icons.Outlined.SignalCellularAlt, + title = TITLE, + dismissButton = BottomAppBarButton(TEXT) {}, + ) {} + } + + composeTestRule.onNodeWithText(TEXT).assertIsDisplayed() + } + + private companion object { + const val TITLE = "Title" + const val TEXT = "Text" + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt index 3acc9ad83d2c..b6d92422c333 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt @@ -18,6 +18,7 @@ package com.android.settingslib.spaprivileged.model.enterprise import android.app.admin.DevicePolicyResources.Strings.Settings import android.content.Context +import android.content.Intent import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.widget.restricted.R @@ -32,6 +33,11 @@ interface BlockedByAdmin : RestrictedMode { fun sendShowAdminSupportDetailsIntent() } +interface BlockedByEcm : RestrictedMode { + fun showRestrictedSettingsDetails() +} + + internal data class BlockedByAdminImpl( private val context: Context, private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin, @@ -55,3 +61,13 @@ internal data class BlockedByAdminImpl( RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin) } } + +internal data class BlockedByEcmImpl( + private val context: Context, + private val intent: Intent, +) : BlockedByEcm { + + override fun showRestrictedSettingsDetails() { + context.startActivity(intent) + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt index 550966beb0b8..9432d5995151 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt @@ -29,10 +29,20 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn +data class EnhancedConfirmation( + val key: String, + val uid: Int, + val packageName: String, +) data class Restrictions( val userId: Int = UserHandle.myUserId(), val keys: List<String>, -) + val enhancedConfirmation: EnhancedConfirmation? = null, +) { + fun isEmpty(): Boolean { + return keys.isEmpty() && enhancedConfirmation == null + } +} interface RestrictionsProvider { @Composable @@ -77,6 +87,14 @@ internal class RestrictionsProviderImpl( .checkIfRestrictionEnforced(context, key, restrictions.userId) ?.let { return BlockedByAdminImpl(context = context, enforcedAdmin = it) } } + + restrictions.enhancedConfirmation?.let { ec -> + RestrictedLockUtilsInternal + .checkIfRequiresEnhancedConfirmation(context, ec.key, + ec.uid, ec.packageName) + ?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) } + } + return NoRestricted } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt index 06b3eabfad26..25c3bc541249 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt @@ -47,6 +47,9 @@ abstract class AppOpPermissionListModel( abstract val appOp: Int abstract val permission: String + override val enhancedConfirmationKey: String? + get() = AppOpsManager.opToPublicName(appOp) + /** * When set, specifies the broader permission who trumps the [permission]. * diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt index 565543614866..1c830c1c5b06 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt @@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.IPackageManagers import com.android.settingslib.spaprivileged.model.app.PackageManagers import com.android.settingslib.spaprivileged.model.app.toRoute +import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -154,7 +155,12 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp override val changeable = { isChangeable } override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) } } - val restrictions = Restrictions(userId, switchRestrictionKeys) + val restrictions = Restrictions(userId = userId, + keys = switchRestrictionKeys, + enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation( + key = it, + uid = checkNotNull(applicationInfo).uid, + packageName = packageName) }) RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory) } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt index 8704f20f5c4a..916d83af3f8f 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt @@ -36,6 +36,9 @@ interface TogglePermissionAppListModel<T : AppRecord> { val switchRestrictionKeys: List<String> get() = emptyList() + val enhancedConfirmationKey: String? + get() = null + /** * Loads the extra info for the App List, and generates the [AppRecord] List. * diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt index 36c91f463efe..4b474379c54b 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt @@ -41,6 +41,7 @@ import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.userId +import com.android.settingslib.spaprivileged.model.enterprise.EnhancedConfirmation import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -149,11 +150,17 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>( @Composable fun getSummary(record: T): () -> String { - val restrictions = remember(record.app.userId) { + val restrictions = remember(record.app.userId, + record.app.uid, record.app.packageName) { Restrictions( userId = record.app.userId, keys = listModel.switchRestrictionKeys, - ) + enhancedConfirmation = listModel.enhancedConfirmationKey?.let { + EnhancedConfirmation( + key = it, + uid = record.app.uid, + packageName = record.app.packageName) + }) } val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions) val allowed = listModel.isAllowed(record) diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt index d5c5574a0450..cd720252e485 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt @@ -40,7 +40,7 @@ internal fun RestrictedSwitchPreference( restrictions: Restrictions, restrictionsProviderFactory: RestrictionsProviderFactory, ) { - if (restrictions.keys.isEmpty()) { + if (restrictions.isEmpty()) { SwitchPreference(model) return } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt index fa44ecb92ed5..aba3460fc1b9 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt @@ -31,6 +31,7 @@ import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin +import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode import com.android.settingslib.spaprivileged.model.enterprise.Restrictions @@ -56,6 +57,7 @@ internal class RestrictedSwitchPreferenceModel( is NoRestricted -> model.checked is BaseUserRestricted -> ({ false }) is BlockedByAdmin -> model.checked + is BlockedByEcm -> model.checked } override val changeable = if (restrictedMode is NoRestricted) model.changeable else ({ false }) @@ -68,24 +70,42 @@ internal class RestrictedSwitchPreferenceModel( is BaseUserRestricted -> model.onCheckedChange // Pass null since semantics ToggleableState is provided in RestrictionWrapper. is BlockedByAdmin -> null + is BlockedByEcm -> null } @Composable fun RestrictionWrapper(content: @Composable () -> Unit) { - if (restrictedMode !is BlockedByAdmin) { - content() - return + when (restrictedMode) { + is BlockedByAdmin -> { + Box( + Modifier + .clickable( + role = Role.Switch, + onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() }, + ) + .semantics { + this.toggleableState = ToggleableState(checked()) + }, + ) { content() } + } + + is BlockedByEcm -> { + Box( + Modifier + .clickable( + role = Role.Switch, + onClick = { restrictedMode.showRestrictedSettingsDetails() }, + ) + .semantics { + this.toggleableState = ToggleableState(checked()) + }, + ) { content() } + } + + else -> { + content() + } } - Box( - Modifier - .clickable( - role = Role.Switch, - onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() }, - ) - .semantics { - this.toggleableState = ToggleableState(checked()) - }, - ) { content() } } private fun ToggleableState(value: Boolean?) = when (value) { @@ -123,6 +143,9 @@ internal class RestrictedSwitchPreferenceModel( context.getString(com.android.settingslib.R.string.disabled) is BlockedByAdmin -> restrictedMode.getSummary(checked()) + is BlockedByEcm -> + context.getString(com.android.settingslib.R.string.disabled) + null -> context.getPlaceholder() } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt index f9abefc11e24..977615b55a6a 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin +import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl @@ -47,6 +48,7 @@ internal fun MoreOptionsScope.RestrictedMenuItemImpl( MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) { when (restrictedMode) { is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent() + is BlockedByEcm -> restrictedMode.showRestrictedSettingsDetails() else -> onClick() } } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 3a258380b3d0..9ae4d0cc13c2 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Gekoppel deur ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Gekoppel deur eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV-verstek"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-uitset"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne luidsprekers"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6dd70fa0dbf4..6c31c7687a55 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"متّصل من خلال ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"متّصل من خلال eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"الجهاز التلقائي لإخراج صوت التلفزيون"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"إخراج الصوت من خلال HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"مكبّرات الصوت الداخلية"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string> <string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index c8eeee330fd9..8b06f5d40aff 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARCৰ জৰিয়তে সংযুক্ত"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARCৰ জৰিয়তে সংযুক্ত"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"টিভি ডিফ’ল্ট"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI আউটপুট"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"অভ্যন্তৰীণ স্পীকাৰ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string> <string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 34f506e94ab3..a36ab27a8f12 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Chrome-da Tətbiqin İşləmə Müddəti vasitəsilə qoşulub"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC vasitəsilə qoşulub"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV defoltu"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI çıxışı"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Daxili dinamiklər"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string> <string name="help_label" msgid="3528360748637781274">"Yardım və rəy"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index ee75df24eae3..83f7c0572887 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano preko ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano preko eARC-a"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Podrazumevana vrednost za TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Unutrašnji zvučnici"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index c17d7236376d..7e8cd8b7b97a 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Падключана праз ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Падключана праз eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартны аўдыявыхад тэлевізара"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Выхад HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Унутраныя дынамікі"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string> <string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 222e758601cf..d279349df5e3 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Свързано посредством ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Свързано посредством eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартното за телевизора"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI изход"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Вградени високоговорители"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string> <string name="help_label" msgid="3528360748637781274">"Помощ и отзиви"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index a7c95dfa4e07..3ac3a3b03c70 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-এর মাধ্যমে কানেক্ট করা হয়েছে"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"টিভির ডিফল্ট সেটিং"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI আউটপুট"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ইন্টার্নাল স্পিকার"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string> <string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 31ea4d406e8f..f84ccb0b1955 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano je putem ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano je putem eARC-a"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Zadano za TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interni zvučnici"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 1bb02c279c73..c205f748a91d 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connectat mitjançant ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connectat mitjançant eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Televisor predeterminat"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortida HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altaveus interns"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 7ab666980285..2a6a247dca36 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Forbundet via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Forbundet via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardindstillinger for fjernsyn"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-udgang"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne højttalere"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string> <string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 29038bcde637..a5f089a7b200 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 29038bcde637..a5f089a7b200 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 29038bcde637..a5f089a7b200 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connected via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connected via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV default"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Internal speakers"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 82cd2bffbae9..c07e1fd5ec42 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Predeterminada de la televisión"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Salida HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altavoces internos"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 35ae73ee7460..ddc16b02ed81 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ühendatud ARC-i kaudu"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ühendatud eARC-i kaudu"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Teleri vaikeväljund"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-väljund"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Sisemised kõlarid"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string> <string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 7ebad6842eb1..deb4bc63ea93 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC bidez konektatuta"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC bidez konektatuta"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Telebistaren audio-erreproduzigailu lehenetsia"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI irteera"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Barneko bozgorailuak"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string> <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 51fd6266d8d1..d73571e2f570 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"متصل ازطریق ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"متصل ازطریق eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"پیشفرض تلویزیون"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"خروجی HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"بلندگوهای داخلی"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string> <string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index f8c9054ac266..3533d77cb8db 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Yhdistetty ARC:n kautta"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Yhdistetty eARC:n kautta"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV:n oletusasetus"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-toisto"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Sisäiset kaiuttimet"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string> <string name="help_label" msgid="3528360748637781274">"Ohje ja palaute"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 2a7266d911d5..06eeae03a46c 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connecté par ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connecté par eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Sortie audio par défaut de la télévision"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Sortie HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Haut-parleurs internes"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string> <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 7b793863f804..f896d16c07f2 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Opción predeterminada da televisión"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altofalantes internos"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index ec534daec56e..50741ddb72da 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC મારફતે કનેક્ટેડ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC મારફતે કનેક્ટેડ"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ડિવાઇસનું ડિફૉલ્ટ ઑડિયો આઉટપુટ, ટીવી"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI આઉટપુટ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"આંતરિક સ્પીકર"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string> <string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 092c729e7504..0a29b6a06835 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"एचडीएमआई eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC से कनेक्ट किए गए डिवाइस"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC से कनेक्ट किए गए डिवाइस"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"टीवी की डिफ़ॉल्ट सेटिंग"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"एचडीएमआई आउटपुट"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"इंटरनल स्पीकर"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string> <string name="help_label" msgid="3528360748637781274">"सहायता और सुझाव"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index eaebfc90af37..b206ac0f59e6 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Povezano putem ARC-a"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Povezano putem eARC-a"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Zadano za TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izlaz"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Unutarnji zvučnici"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index d287ce35b58a..68ff9ae62d18 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Csatlakoztatva ARC-n keresztül"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Csatlakoztatva eARC-n keresztül"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tévé alapértelmezett eszköze"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-kimenet"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Belső hangszórók"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string> <string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 4d2709961857..f3061a704f0b 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Միացված է ARC-ի միջոցով"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Միացված է eARC-ի միջոցով"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Հեռուստացույցի կանխադրված կարգավորումներ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ելք"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ներքին բարձրախոսներ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string> <string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index ce770af09438..2c166039b38a 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Terhubung melalui ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Terhubung melalui eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV sebagai default"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Output HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Speaker internal"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat & aktifkan kembali"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & masukan"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index fd68d1405a28..5a82e16bb9a4 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"eARC HDMI"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Connessione tramite ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Connessione tramite eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV predefinita"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Uscita HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Speaker interni"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string> <string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 58d4f0ec8388..5d578431e9ba 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC 経由で接続済み"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC 経由で接続済み"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"テレビのデフォルト"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 出力"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"内蔵スピーカー"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string> <string name="help_label" msgid="3528360748637781274">"ヘルプとフィードバック"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 0d31dde9d87b..a8fb9aa6054d 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC арқылы жалғанған."</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC арқылы жалғанған."</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Теледидардың әдепкі шығысы"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI шығыс интерфейсі"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ішкі динамиктер"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string> <string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 8ee7987cc78b..16f14ff6c906 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"បានភ្ជាប់តាមរយៈ ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"បានភ្ជាប់តាមរយៈ eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"លំនាំដើមទូរទស្សន៍"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"ធាតុចេញ HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ឧបករណ៍បំពងសំឡេងខាងក្នុង"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មានបញ្ហាក្នុងការភ្ជាប់។ បិទ រួចបើកឧបករណ៍វិញ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍សំឡេងប្រើខ្សែ"</string> <string name="help_label" msgid="3528360748637781274">"ជំនួយ និងមតិកែលម្អ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 73f7a20cc706..640d4e949a94 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ಮೂಲಕ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ಟಿವಿ ಡೀಫಾಲ್ಟ್"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ಔಟ್ಪುಟ್"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ಆಂತರಿಕ ಸ್ಪೀಕರ್ಗಳು"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string> <string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 418e47e41ce5..1bf0a5130f32 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC를 통해 연결됨"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC를 통해 연결됨"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV 기본값"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 출력"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"내부 스피커"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string> <string name="help_label" msgid="3528360748637781274">"고객센터"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 182e912fe74c..9565fb0ce64b 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC аркылуу туташты"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC аркылуу туташты"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Cыналгыдагы демейки түзмөк"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI аудио түзмөгү"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ички динамиктер"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string> <string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 5f8ba5fe135d..23f249a0dfc4 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ເຊື່ອມຕໍ່ຜ່ານ ARC ແລ້ວ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"ເຊື່ອມຕໍ່ຜ່ານ eARC ແລ້ວ"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ຄ່າເລີ່ມຕົ້ນສຳລັບໂທລະທັດ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"ເອົ້າພຸດ HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ລຳໂພງຂອງເຄື່ອງ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string> <string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 5294f4abdb35..f529ca48deb9 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI „eARC“"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Prisijungta per ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Prisijungta per „eARC“"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV numatytoji išvestis"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI išvestis"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Vidiniai garsiakalbiai"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string> <string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 7ea01e73b884..24be0aaef92f 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Savienojums izveidots, izmantojot ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Savienojums izveidots, izmantojot eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Noklusējuma iestatījums televizorā"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI izvade"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Iekšējie skaļruņi"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string> <string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index d38f42c9fb3b..030a438576b0 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Поврзано преку ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Поврзано преку eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандарден излез на телевизор"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Излез за HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внатрешни звучници"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string> <string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 8b6150d50c5c..9a01b5333d7c 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC വഴി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ടിവി ഡിഫോൾട്ട്"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ഔട്ട്പുട്ട്"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ആന്തരിക സ്പീക്കറുകൾ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്റ്റ് ചെയ്യുന്നതിൽ പ്രശ്നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string> <string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്ബാക്കും"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index e7c83a87ea45..03fecc411d9d 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC-р холбогдсон"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC-р холбогдсон"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ТВ-ийн өгөгдмөл"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI гаралт"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Дотоод чанга яригч"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string> <string name="help_label" msgid="3528360748637781274">"Тусламж, санал хүсэлт"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 381d0b861324..e4edf088a002 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC द्वारे कनेक्ट केलेली"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC द्वारे कनेक्ट केलेली"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"टीव्ही डीफॉल्ट"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI आउटपुट"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"अंतर्गत स्पीकर"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करण्यात समस्या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string> <string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 3e7d901addd6..88f13f6f4797 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Disambungkan melalui ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Disambungkan melalui eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tetapan lalai TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Output HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Pembesar suara dalaman"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan & hidupkan kembali peranti"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & maklum balas"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 68ae54962991..58e2cf44bd0a 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV မူရင်း"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI အထွက်"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"စက်ရှိ စပီကာများ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string> <string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index a11bfff7a31f..a5bfe5e1d893 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Tilkoblet via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Tilkoblet via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV-ens standardinnstilling"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-utgang"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne høyttalere"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string> <string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index b833c140e996..d8e11522f4f1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC मार्फत कनेक्ट गरिएका"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC मार्फत कनेक्ट गरिएका"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"टिभीको डिफल्ट अडियो आउटपुट"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI आउटपुट"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"आन्तरिक स्पिकरहरू"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि अन गर्नुहोस्"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string> <string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f9215e547cfe..a65ff730dc8c 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Verbonden via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Verbonden via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Tv-standaard"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-uitvoer"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interne speakers"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en feedback"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index c4ed00585cb9..add9ff708d5e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ମାଧ୍ୟମରେ କନେକ୍ଟ କରାଯାଇଛି"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ଟିଭିର ଡିଫଲ୍ଟ ଅଡିଓ ଆଉଟପୁଟ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ଆଉଟପୁଟ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ଇଣ୍ଟର୍ନଲ ସ୍ପିକର"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string> <string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index b7a67fd4a5ac..4a33f050c0d0 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤੇ ਗਏ ਡੀਵਾਈਸ"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ਟੀਵੀ ਦਾ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਆਊਟਪੁੱਟ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ਆਊਟਪੁੱਟ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ਅੰਦਰੂਨੀ ਸਪੀਕਰ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string> <string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 3ef8fd7e01f3..b319cab8395a 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Połączono przez ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Połączono przez eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Ustawienie domyślne telewizora"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Wyjście HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Głośniki wewnętrzne"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string> <string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index b76b37bb8e05..32fa1b621e01 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -237,8 +237,8 @@ <string name="adb_wireless_error" msgid="721958772149779856">"Erro"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Depuração por Wi-Fi"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para acessar e usar dispositivos disponíveis, ative a depuração por Wi-Fi."</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Parear o dispositivo com um código QR"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Parear novos dispositivos usando um leitor de código QR"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Parear o dispositivo com um QR code"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Parear novos dispositivos usando um leitor de QR code"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Parear o dispositivo com um código de pareamento"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Parear novos dispositivos usando um código de seis dígitos"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Dispositivos pareados"</string> @@ -252,12 +252,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento por Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Falha no pareamento"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Verifique se o dispositivo está conectado à mesma rede."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pareando dispositivo…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Falha ao parear o dispositivo. O código QR está incorreto ou o dispositivo não está conectado à mesma rede."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Falha ao parear o dispositivo. O QR code está incorreto ou o dispositivo não está conectado à mesma rede."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Endereço IP e porta"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Ler código QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Ler QR code"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Conecte-se a uma rede Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Atalho para relatório de bugs"</string> @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Padrão da TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Alto-falantes internos"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index b76b37bb8e05..32fa1b621e01 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -237,8 +237,8 @@ <string name="adb_wireless_error" msgid="721958772149779856">"Erro"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Depuração por Wi-Fi"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Para acessar e usar dispositivos disponíveis, ative a depuração por Wi-Fi."</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Parear o dispositivo com um código QR"</string> - <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Parear novos dispositivos usando um leitor de código QR"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Parear o dispositivo com um QR code"</string> + <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Parear novos dispositivos usando um leitor de QR code"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Parear o dispositivo com um código de pareamento"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Parear novos dispositivos usando um código de seis dígitos"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"Dispositivos pareados"</string> @@ -252,12 +252,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento por Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Falha no pareamento"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Verifique se o dispositivo está conectado à mesma rede."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do QR code"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Pareando dispositivo…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Falha ao parear o dispositivo. O código QR está incorreto ou o dispositivo não está conectado à mesma rede."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Falha ao parear o dispositivo. O QR code está incorreto ou o dispositivo não está conectado à mesma rede."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Endereço IP e porta"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Ler código QR"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Ler QR code"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do QR code"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Conecte-se a uma rede Wi-Fi"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Atalho para relatório de bugs"</string> @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Padrão da TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Saída HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Alto-falantes internos"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 862e3d4c2df4..fe65be5114b0 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Подключено через ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Подключено через eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартный выход на телевизоре"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Выход HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внутренние динамики"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string> <string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 795abec84d40..55e0778662ea 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC හරහා සම්බන්ධ කර ඇත"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC හරහා සම්බන්ධ කර ඇත"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"රූපවාහිනී පෙරනිමිය"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI ප්රතිදානය"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"අභ්යන්තර ස්පීකර්"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්රියාවිරහිත කර & ආපසු ක්රියාත්මක කරන්න"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string> <string name="help_label" msgid="3528360748637781274">"උදවු & ප්රතිපෝෂණ"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 00c0bc1c5c00..4d03ddd79201 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Pripojené prostredníctvom rozhrania ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Pripojené prostredníctvom rozhrania eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Predvolené nastavenie televízora"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Výstup HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interné reproduktory"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string> <string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index f20401744b8e..b627d097055b 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Lidhur përmes ARC-së"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Lidhur përmes eARC-së"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Parazgjedhja e televizorit"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Dalja HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Altoparlantët e brendshëm"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string> <string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index b83986a8ac42..224be52fb23d 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Повезано преко ARC-а"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Повезано преко eARC-а"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Подразумевана вредност за ТВ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI излаз"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Унутрашњи звучници"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string> <string name="help_label" msgid="3528360748637781274">"Помоћ и повратне информације"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 58e3705e0ec3..7ffad7c387b7 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ansluten via ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ansluten via eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Standardutgång för tv:n"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI-utgång"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Interna högtalare"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string> <string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 2a2536b2cbfa..b76f7e34fbb7 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC மூலம் இணைக்கப்பட்டுள்ளவை"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC மூலம் இணைக்கப்பட்டுள்ளவை"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"டிவி இயல்புநிலை"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI அவுட்புட்"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"உட்புற ஸ்பீக்கர்கள்"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string> <string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 303d31044b6b..23c00c11e866 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ద్వారా కనెక్ట్ చేయబడింది"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ద్వారా కనెక్ట్ చేయబడింది"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"టీవీ ఆటోమేటిక్ సెట్టింగ్"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI అవుట్పుట్"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"అంతర్గత స్పీకర్లు"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 0ac8cb858a2c..4d9d382c83ba 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"เชื่อมต่อผ่าน ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"เชื่อมต่อผ่าน eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"ค่าเริ่มต้นของทีวี"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"เอาต์พุต HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"ลำโพงของเครื่อง"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string> <string name="help_label" msgid="3528360748637781274">"ความช่วยเหลือและความคิดเห็น"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 4f92e1d1bab1..c9e34941cfe1 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Nakakonekta sa pamamagitan ng ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Nakakonekta sa pamamagitan ng eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Default ng TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI output"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Mga internal speaker"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string> <string name="help_label" msgid="3528360748637781274">"Tulong at feedback"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 795bb303e292..0f628b44fe69 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC ile bağlandı"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC ile bağlandı"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV varsayılanı"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI çıkışı"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Dahili hoparlörler"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string> <string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index beb2c917e923..e00fbc990461 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Підключено через ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Підключено через eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Стандартний вихід телевізора"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Вихід HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Внутрішні динаміки"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string> <string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 13718d94eee0..0dcb12fb0e64 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC کے ذریعے منسلک ہے"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC کے ذریعے منسلک ہے"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"TV ڈیفالٹ"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI آؤٹ پٹ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"اندرونی اسپیکرز"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string> <string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 7559a3dbd89f..c94ff4d1a8f0 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC orqali ulangan"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC orqali ulangan"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Televizorda birlamchi"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI chiqishi"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Ichki karnaylar"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string> <string name="help_label" msgid="3528360748637781274">"Yordam/fikr-mulohaza"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index ccdc0bc96cc4..03f0770bad8d 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Đã kết nối qua ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Đã kết nối qua eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"Chế độ mặc định của TV"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"Đầu ra HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Các loa trong"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string> <string name="help_label" msgid="3528360748637781274">"Trợ giúp và phản hồi"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 6b3e06104efa..30db0331f99f 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已通过 ARC 连接"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已通过 eARC 连接"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"电视默认设置"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 输出"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"内置扬声器"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string> <string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 35171090b977..2ce4de9ee7e0 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"已透過 ARC 連接"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"已透過 eARC 連接"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"電視預設的音訊輸出"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 輸出"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"內置喇叭"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string> <string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index eeed9f3ea5d4..2fac076a51cc 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"透過 ARC 連線"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"透過 eARC 連線"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"電視預設的音訊輸出裝置"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"HDMI 輸出裝置"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"內建揚聲器"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string> <string name="help_label" msgid="3528360748637781274">"說明與意見回饋"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index e94b9299e187..9a818082a624 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -567,12 +567,9 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"I-HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ixhunywe nge-ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ixhunywe nge-eARC"</string> - <!-- no translation found for tv_media_transfer_default (5403053145185843843) --> - <skip /> - <!-- no translation found for tv_media_transfer_hdmi (692569220956829921) --> - <skip /> - <!-- no translation found for tv_media_transfer_internal_speakers (8181494402866565865) --> - <skip /> + <string name="tv_media_transfer_default" msgid="5403053145185843843">"I-TV ezenzakalelayo"</string> + <string name="tv_media_transfer_hdmi" msgid="692569220956829921">"umphumela we-HDMI"</string> + <string name="tv_media_transfer_internal_speakers" msgid="8181494402866565865">"Izipikha zangaphakathi"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string> <string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index e77964c6fa0c..4454b710b7e4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -22,13 +22,16 @@ import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AF import static com.android.settingslib.Utils.getColorAttrDefaultColor; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; @@ -39,10 +42,12 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; +import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; +import android.util.ArraySet; import android.util.Log; import android.view.MenuItem; import android.widget.TextView; @@ -53,6 +58,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import java.util.List; +import java.util.Set; /** * Utility class to host methods usable in adding a restricted padlock icon and showing admin @@ -62,6 +68,16 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { private static final String LOG_TAG = "RestrictedLockUtils"; private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); + private static final Set<String> ECM_KEYS = new ArraySet<>(); + + static { + if (android.security.Flags.extendEcmToAllSettings()) { + ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); + ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); + ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); + ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN); + } + } /** * @return drawables for displaying with settings that are locked by a device admin. @@ -81,6 +97,38 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** + * Checks if a given permission requires additional confirmation for the given package + * + * @return An intent to show the user if additional confirmation is required, null otherwise + */ + @Nullable + public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context, + @NonNull String restriction, + int uid, + @Nullable String packageName) { + // TODO(b/297372999): Replace with call to mainline module once ready + + if (!ECM_KEYS.contains(restriction)) { + return null; + } + + final AppOpsManager appOps = (AppOpsManager) context + .getSystemService(Context.APP_OPS_SERVICE); + final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, + uid, packageName, null, null); + final boolean ecmEnabled = context.getResources().getBoolean( + com.android.internal.R.bool.config_enhancedConfirmationModeEnabled); + if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) { + final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(Intent.EXTRA_UID, uid); + return intent; + } + + return null; + } + + /** * Checks if a restriction is enforced on a user and returns the enforced admin and * admin userId. * diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java index 5860bdabe764..4384400eaa88 100644 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java +++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java @@ -89,8 +89,14 @@ public class InputMethodSettingValuesWrapper { public void refreshAllInputMethodAndSubtypes() { mMethodList.clear(); - mMethodList.addAll(mImm.getInputMethodListAsUser( - mContentResolver.getUserId(), DirectBootAwareness.ANY)); + List<InputMethodInfo> imis = mImm.getInputMethodListAsUser( + mContentResolver.getUserId(), DirectBootAwareness.ANY); + for (int i = 0; i < imis.size(); ++i) { + InputMethodInfo imi = imis.get(i); + if (!imi.isVirtualDeviceOnly()) { + mMethodList.add(imi); + } + } } public List<InputMethodInfo> getInputMethodList() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java index 25833b3ddbba..5aee8cdb8b9e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java @@ -16,7 +16,7 @@ package com.android.settingslib.core.instrumentation; -import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_TOGGLE; +import static com.android.internal.jank.Cuj.CUJ_SETTINGS_TOGGLE; import static com.android.settingslib.core.instrumentation.SettingsJankMonitor.MONITORED_ANIMATION_DURATION_MS; import static com.google.common.truth.Truth.assertThat; @@ -34,8 +34,8 @@ import androidx.preference.PreferenceGroupAdapter; import androidx.preference.SwitchPreference; import androidx.recyclerview.widget.RecyclerView; +import com.android.internal.jank.Cuj.CujType; import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.jank.InteractionJankMonitor.CujType; import com.android.settingslib.testutils.OverpoweredReflectionHelper; import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 7cec99d4189f..8f459c647316 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -95,7 +95,9 @@ final class SettingsState { static final int SETTINGS_VERSION_NEW_ENCODING = 121; + // LINT.IfChange public static final int MAX_LENGTH_PER_STRING = 32768; + // LINT.ThenChange(/services/core/java/com/android/server/audio/AudioDeviceInventory.java:settings_max_length_per_string) private static final long WRITE_SETTINGS_DELAY_MILLIS = 200; private static final long MAX_WRITE_SETTINGS_DELAY_MILLIS = 2000; diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c2c5e001a5df..7061e2cb8a4e 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -281,6 +281,7 @@ android_library { "com_android_systemui_flags_lib", "com_android_systemui_shared_flags_lib", "flag-junit-base", + "platform-parametric-runner-lib", "androidx.viewpager2_viewpager2", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp index 8b12f3c738cc..6c75b4346739 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp @@ -59,6 +59,4 @@ android_app { platform_apis: true, resource_dirs: ["res"], certificate: "platform", - // This app uses allowlisted privileged permissions. - privileged: true, } diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp index e842967e6150..50ed7ab7c85a 100644 --- a/packages/SystemUI/aconfig/Android.bp +++ b/packages/SystemUI/aconfig/Android.bp @@ -23,6 +23,7 @@ package { default_visibility: [ "//visibility:override", "//frameworks/base/packages/SystemUI:__subpackages__", + "//platform_testing:__subpackages__" ], } diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index c52a89c9b05e..a26b311b1f8f 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -138,6 +138,13 @@ flag { } flag { + name: "qs_new_tiles" + namespace: "systemui" + description: "Use the new tiles in the Quick Settings. Should have no behavior changes." + bug: "241772429" +} + +flag { name: "coroutine_tracing" namespace: "systemui" description: "Adds thread-local data to System UI's global coroutine scopes to " @@ -204,6 +211,13 @@ flag { } flag { + name: "enable_layout_tracing" + namespace: "systemui" + description: "Enables detailed traversal slices during measure and layout in perfetto traces" + bug: "315274804" +} + +flag { name: "quick_settings_visual_haptics_longpress" namespace: "systemui" description: "Enable special visual and haptic effects for quick settings tiles with long-press actions" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 187d0739b734..168039ed5d3d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -33,8 +33,8 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS import com.android.app.animation.Interpolators +import com.android.internal.jank.Cuj.CujType import com.android.internal.jank.InteractionJankMonitor -import com.android.internal.jank.InteractionJankMonitor.CujType import com.android.systemui.util.maybeForceFullscreen import com.android.systemui.util.registerAnimationOnBackInvoked import kotlin.math.roundToInt diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index af35ea44322f..b738e2bc972b 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -33,6 +33,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay import android.widget.FrameLayout +import com.android.internal.jank.Cuj.CujType import com.android.internal.jank.InteractionJankMonitor import java.util.LinkedList import kotlin.math.min @@ -58,7 +59,7 @@ constructor( /** The view that will be ghosted and from which the background will be extracted. */ private val ghostedView: View, - /** The [InteractionJankMonitor.CujType] associated to this animation. */ + /** The [CujType] associated to this animation. */ private val cujType: Int? = null, private var interactionJankMonitor: InteractionJankMonitor = InteractionJankMonitor.getInstance(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt index 1a653c316db1..47f5663f25a7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalFoundationApi::class) + package com.android.systemui.bouncer.ui.composable import android.app.AlertDialog @@ -29,18 +31,18 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown @@ -51,6 +53,7 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -78,6 +81,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.transitions import com.android.compose.modifiers.thenIf +import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel @@ -100,37 +104,41 @@ fun BouncerContent( dialogFactory: BouncerDialogFactory, modifier: Modifier = Modifier, ) { - val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState() val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported) - when (layout) { - BouncerSceneLayout.STANDARD -> - StandardLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - modifier = modifier, - ) - BouncerSceneLayout.SIDE_BY_SIDE -> - SideBySideLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - isUserSwitcherVisible = isFullScreenUserSwitcherEnabled, - modifier = modifier, - ) - BouncerSceneLayout.STACKED -> - StackedLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - isUserSwitcherVisible = isFullScreenUserSwitcherEnabled, - modifier = modifier, - ) - BouncerSceneLayout.SPLIT -> - SplitLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - modifier = modifier, - ) + Box( + // Allows the content within each of the layouts to react to the appearance and + // disappearance of the IME, which is also known as the software keyboard. + // + // Despite the keyboard only being part of the password bouncer, adding it at this level is + // both necessary to properly handle the keyboard in all layouts and harmless in cases when + // the keyboard isn't used (like the PIN or pattern auth methods). + modifier = modifier.imePadding(), + ) { + when (layout) { + BouncerSceneLayout.STANDARD_BOUNCER -> + StandardLayout( + viewModel = viewModel, + ) + BouncerSceneLayout.BESIDE_USER_SWITCHER -> + BesideUserSwitcherLayout( + viewModel = viewModel, + ) + BouncerSceneLayout.BELOW_USER_SWITCHER -> + BelowUserSwitcherLayout( + viewModel = viewModel, + ) + BouncerSceneLayout.SPLIT_BOUNCER -> + SplitLayout( + viewModel = viewModel, + ) + } + + Dialog( + viewModel = viewModel, + dialogFactory = dialogFactory, + ) } } @@ -141,15 +149,333 @@ fun BouncerContent( @Composable private fun StandardLayout( viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, modifier: Modifier = Modifier, - layout: BouncerSceneLayout = BouncerSceneLayout.STANDARD, - outputOnly: Boolean = false, +) { + val isHeightExpanded = + LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded + + FoldAware( + modifier = + modifier.padding( + top = 92.dp, + bottom = 48.dp, + ), + viewModel = viewModel, + aboveFold = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(), + ) { + StatusMessage( + viewModel = viewModel, + modifier = Modifier, + ) + + OutputArea( + viewModel = viewModel, + modifier = Modifier.padding(top = if (isHeightExpanded) 96.dp else 64.dp), + ) + } + }, + belowFold = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(), + ) { + Box( + modifier = Modifier.weight(1f), + ) { + InputArea( + viewModel = viewModel, + pinButtonRowVerticalSpacing = 12.dp, + centerPatternDotsVertically = false, + modifier = Modifier.align(Alignment.BottomCenter), + ) + } + + ActionArea( + viewModel = viewModel, + modifier = Modifier.padding(top = 48.dp), + ) + } + }, + ) +} + +/** + * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable + * by double-tapping on the side. + */ +@Composable +private fun SplitLayout( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + val authMethod by viewModel.authMethodViewModel.collectAsState() + + Row( + modifier = + modifier + .fillMaxHeight() + .padding( + horizontal = 24.dp, + vertical = if (authMethod is PasswordBouncerViewModel) 24.dp else 48.dp, + ), + ) { + // Left side (in left-to-right locales). + Box( + modifier = Modifier.fillMaxHeight().weight(1f), + ) { + when (authMethod) { + is PinBouncerViewModel -> { + StatusMessage( + viewModel = viewModel, + modifier = Modifier.align(Alignment.TopCenter), + ) + + OutputArea(viewModel = viewModel, modifier = Modifier.align(Alignment.Center)) + + ActionArea( + viewModel = viewModel, + modifier = Modifier.align(Alignment.BottomCenter).padding(top = 48.dp), + ) + } + is PatternBouncerViewModel -> { + StatusMessage( + viewModel = viewModel, + modifier = Modifier.align(Alignment.TopCenter), + ) + + ActionArea( + viewModel = viewModel, + modifier = Modifier.align(Alignment.BottomCenter).padding(vertical = 48.dp), + ) + } + is PasswordBouncerViewModel -> { + ActionArea( + viewModel = viewModel, + modifier = Modifier.align(Alignment.BottomCenter), + ) + } + else -> Unit + } + } + + // Right side (in right-to-left locales). + Box( + modifier = Modifier.fillMaxHeight().weight(1f), + ) { + when (authMethod) { + is PinBouncerViewModel, + is PatternBouncerViewModel -> { + InputArea( + viewModel = viewModel, + pinButtonRowVerticalSpacing = 8.dp, + centerPatternDotsVertically = true, + modifier = Modifier.align(Alignment.Center), + ) + } + is PasswordBouncerViewModel -> { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth().align(Alignment.Center), + ) { + StatusMessage( + viewModel = viewModel, + ) + + OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp)) + } + } + else -> Unit + } + } + } +} + +/** + * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap + * anywhere on the background to flip their positions. + */ +@Composable +private fun BesideUserSwitcherLayout( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + val layoutDirection = LocalLayoutDirection.current + val isLeftToRight = layoutDirection == LayoutDirection.Ltr + val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) } + val isHeightExpanded = + LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded + val authMethod by viewModel.authMethodViewModel.collectAsState() + + Row( + modifier = + modifier.pointerInput(Unit) { + detectTapGestures( + onDoubleTap = { offset -> + // Depending on where the user double tapped, switch the elements such that + // the endContent is closer to the side that was double tapped. + setSwapped(offset.x < size.width / 2) + } + ) + }, + ) { + val animatedOffset by + animateFloatAsState( + targetValue = + if (!isSwapped) { + // A non-swapped element has its natural placement so it's not offset. + 0f + } else if (isLeftToRight) { + // A swapped element has its elements offset horizontally. In the case of + // LTR locales, this means pushing the element to the right, hence the + // positive number. + 1f + } else { + // A swapped element has its elements offset horizontally. In the case of + // RTL locales, this means pushing the element to the left, hence the + // negative number. + -1f + }, + label = "offset", + ) + + fun Modifier.swappable(inversed: Boolean = false): Modifier { + return graphicsLayer { + translationX = + size.width * + animatedOffset * + if (inversed) { + // A negative sign is used to make sure this is offset in the direction + // that's opposite to the direction that the user switcher is pushed in. + -1 + } else { + 1 + } + alpha = animatedAlpha(animatedOffset) + } + } + + UserSwitcher( + viewModel = viewModel, + modifier = Modifier.weight(1f).align(Alignment.CenterVertically).swappable(), + ) + + FoldAware( + modifier = + Modifier.weight(1f) + .padding( + if (isHeightExpanded) { + PaddingValues(vertical = 128.dp) + } else { + PaddingValues(top = 94.dp, bottom = 48.dp) + } + ) + .swappable(inversed = true), + viewModel = viewModel, + aboveFold = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + StatusMessage( + viewModel = viewModel, + ) + + OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp)) + } + }, + belowFold = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(), + ) { + val isOutputAreaVisible = authMethod !is PatternBouncerViewModel + // If there is an output area and the window is not tall enough, spacing needs + // to be added between the input and the output areas (otherwise the two get + // very squished together). + val addSpacingBetweenOutputAndInput = isOutputAreaVisible && !isHeightExpanded + + Box( + modifier = + Modifier.weight(1f) + .padding(top = (if (addSpacingBetweenOutputAndInput) 24 else 0).dp), + ) { + InputArea( + viewModel = viewModel, + pinButtonRowVerticalSpacing = 12.dp, + centerPatternDotsVertically = true, + modifier = Modifier.align(Alignment.BottomCenter), + ) + } + + ActionArea( + viewModel = viewModel, + modifier = Modifier.padding(top = 48.dp), + ) + } + }, + ) + } +} + +/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */ +@Composable +private fun BelowUserSwitcherLayout( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + Column( + modifier = + modifier.padding( + vertical = 128.dp, + ) + ) { + UserSwitcher( + viewModel = viewModel, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(Modifier.weight(1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(), + ) { + StatusMessage( + viewModel = viewModel, + ) + + OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp)) + + InputArea( + viewModel = viewModel, + pinButtonRowVerticalSpacing = 12.dp, + centerPatternDotsVertically = true, + modifier = Modifier.padding(top = 128.dp), + ) + + ActionArea( + viewModel = viewModel, + modifier = Modifier.padding(top = 48.dp), + ) + } + } + } +} + +@Composable +private fun FoldAware( + viewModel: BouncerViewModel, + aboveFold: @Composable BoxScope.() -> Unit, + belowFold: @Composable BoxScope.() -> Unit, + modifier: Modifier = Modifier, ) { val foldPosture: FoldPosture by foldPosture() val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState() - val isSplitAroundTheFold = - foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired + val isSplitAroundTheFold = foldPosture == FoldPosture.Tabletop && isSplitAroundTheFoldRequired val currentSceneKey = if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey @@ -160,115 +486,57 @@ private fun StandardLayout( modifier = modifier, ) { scene(SceneKeys.ContiguousSceneKey) { - FoldSplittable( - viewModel = viewModel, - dialogFactory = dialogFactory, - layout = layout, - outputOnly = outputOnly, + FoldableScene( + aboveFold = aboveFold, + belowFold = belowFold, isSplit = false, ) } scene(SceneKeys.SplitSceneKey) { - FoldSplittable( - viewModel = viewModel, - dialogFactory = dialogFactory, - layout = layout, - outputOnly = outputOnly, + FoldableScene( + aboveFold = aboveFold, + belowFold = belowFold, isSplit = true, ) } } } -/** - * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user - * switcher UI) and laid out vertically, centered horizontally. - * - * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't - * render across the location of the fold hardware when the device is fully or part-way unfolded - * with the fold hinge in a horizontal position. - * - * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN - * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter - * their PIN or pattern. - */ @Composable -private fun SceneScope.FoldSplittable( - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - layout: BouncerSceneLayout, - outputOnly: Boolean, +private fun SceneScope.FoldableScene( + aboveFold: @Composable BoxScope.() -> Unit, + belowFold: @Composable BoxScope.() -> Unit, isSplit: Boolean, modifier: Modifier = Modifier, ) { - val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState() - val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState() - var dialog: Dialog? by remember { mutableStateOf(null) } - val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState() val splitRatio = LocalContext.current.resources.getFloat( R.dimen.motion_layout_half_fold_bouncer_height_ratio ) - Column(modifier = modifier.padding(horizontal = 32.dp)) { + Column( + modifier = modifier.fillMaxHeight(), + ) { // Content above the fold, when split on a foldable device in a "table top" posture: Box( modifier = Modifier.element(SceneElements.AboveFold) - .fillMaxWidth() .then( if (isSplit) { Modifier.weight(splitRatio) - } else if (outputOnly) { - Modifier.fillMaxHeight() } else { Modifier } ), ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().padding(top = layout.topPadding), - ) { - Crossfade( - targetState = message, - label = "Bouncer message", - animationSpec = if (message.isUpdateAnimated) tween() else snap(), - ) { message -> - Text( - text = message.text, - color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.bodyLarge, - ) - } - - if (!outputOnly) { - Spacer(Modifier.height(layout.spacingBetweenMessageAndEnteredInput)) - - UserInputArea( - viewModel = viewModel, - visibility = UserInputAreaVisibility.OUTPUT_ONLY, - layout = layout, - ) - } - } - - if (outputOnly) { - UserInputArea( - viewModel = viewModel, - visibility = UserInputAreaVisibility.OUTPUT_ONLY, - layout = layout, - modifier = Modifier.align(Alignment.Center), - ) - } + aboveFold() } // Content below the fold, when split on a foldable device in a "table top" posture: Box( modifier = Modifier.element(SceneElements.BelowFold) - .fillMaxWidth() .weight( if (isSplit) { 1 - splitRatio @@ -277,73 +545,40 @@ private fun SceneScope.FoldSplittable( } ), ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxSize() - ) { - if (!outputOnly) { - Box(Modifier.weight(1f)) { - UserInputArea( - viewModel = viewModel, - visibility = UserInputAreaVisibility.INPUT_ONLY, - layout = layout, - modifier = Modifier.align(Alignment.BottomCenter), - ) - } - } - - Spacer(Modifier.height(48.dp)) - - val actionButtonModifier = Modifier.height(56.dp) - - actionButton.let { actionButtonViewModel -> - if (actionButtonViewModel != null) { - BouncerActionButton( - viewModel = actionButtonViewModel, - modifier = actionButtonModifier, - ) - } else { - Spacer(modifier = actionButtonModifier) - } - } - - Spacer(Modifier.height(layout.bottomPadding)) - } + belowFold() } + } +} - if (dialogMessage != null) { - if (dialog == null) { - dialog = - dialogFactory().apply { - setMessage(dialogMessage) - setButton( - DialogInterface.BUTTON_NEUTRAL, - context.getString(R.string.ok), - ) { _, _ -> - viewModel.onThrottlingDialogDismissed() - } - setCancelable(false) - setCanceledOnTouchOutside(false) - show() - } - } - } else { - dialog?.dismiss() - dialog = null - } +@Composable +private fun StatusMessage( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState() + + Crossfade( + targetState = message, + label = "Bouncer message", + animationSpec = if (message.isUpdateAnimated) tween() else snap(), + modifier = modifier, + ) { + Text( + text = it.text, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyLarge, + ) } } /** - * Renders the user input area, where the user interacts with the UI to enter their credentials. + * Renders the user output area, where the user sees what they entered. * - * For example, this can be the pattern input area, the password text box, or pin pad. + * For example, this can be the PIN shapes or password text field. */ @Composable -private fun UserInputArea( +private fun OutputArea( viewModel: BouncerViewModel, - visibility: UserInputAreaVisibility, - layout: BouncerSceneLayout, modifier: Modifier = Modifier, ) { val authMethodViewModel: AuthMethodBouncerViewModel? by @@ -351,66 +586,115 @@ private fun UserInputArea( when (val nonNullViewModel = authMethodViewModel) { is PinBouncerViewModel -> - when (visibility) { - UserInputAreaVisibility.OUTPUT_ONLY -> - PinInputDisplay( - viewModel = nonNullViewModel, - modifier = modifier, - ) - UserInputAreaVisibility.INPUT_ONLY -> - PinPad( - viewModel = nonNullViewModel, - layout = layout, - modifier = modifier, - ) - } + PinInputDisplay( + viewModel = nonNullViewModel, + modifier = modifier, + ) is PasswordBouncerViewModel -> - if (visibility == UserInputAreaVisibility.INPUT_ONLY) { - PasswordBouncer( - viewModel = nonNullViewModel, - modifier = modifier, - ) - } - is PatternBouncerViewModel -> - if (visibility == UserInputAreaVisibility.INPUT_ONLY) { - PatternBouncer( - viewModel = nonNullViewModel, - layout = layout, - modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false), - ) - } + PasswordBouncer( + viewModel = nonNullViewModel, + modifier = modifier, + ) else -> Unit } } /** - * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call. + * Renders the user input area, where the user enters their credentials. + * + * For example, this can be the pattern input area or the PIN pad. */ -@OptIn(ExperimentalFoundationApi::class) @Composable -private fun BouncerActionButton( - viewModel: BouncerActionButtonModel, +private fun InputArea( + viewModel: BouncerViewModel, + pinButtonRowVerticalSpacing: Dp, + centerPatternDotsVertically: Boolean, modifier: Modifier = Modifier, ) { - Button( - onClick = viewModel.onClick, - modifier = - modifier.thenIf(viewModel.onLongClick != null) { - Modifier.combinedClickable( - onClick = viewModel.onClick, - onLongClick = viewModel.onLongClick, + val authMethodViewModel: AuthMethodBouncerViewModel? by + viewModel.authMethodViewModel.collectAsState() + + when (val nonNullViewModel = authMethodViewModel) { + is PinBouncerViewModel -> { + PinPad( + viewModel = nonNullViewModel, + verticalSpacing = pinButtonRowVerticalSpacing, + modifier = modifier, + ) + } + is PatternBouncerViewModel -> { + PatternBouncer( + viewModel = nonNullViewModel, + centerDotsVertically = centerPatternDotsVertically, + modifier = modifier, + ) + } + else -> Unit + } +} + +@Composable +private fun ActionArea( + viewModel: BouncerViewModel, + modifier: Modifier = Modifier, +) { + val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState() + + actionButton?.let { actionButtonViewModel -> + Box( + modifier = modifier, + ) { + Button( + onClick = actionButtonViewModel.onClick, + modifier = + Modifier.height(56.dp).thenIf(actionButtonViewModel.onLongClick != null) { + Modifier.combinedClickable( + onClick = actionButtonViewModel.onClick, + onLongClick = actionButtonViewModel.onLongClick, + ) + }, + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + ), + ) { + Text( + text = actionButtonViewModel.label, + style = MaterialTheme.typography.bodyMedium, ) - }, - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.tertiaryContainer, - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, - ), - ) { - Text( - text = viewModel.label, - style = MaterialTheme.typography.bodyMedium, - ) + } + } + } +} + +@Composable +private fun Dialog( + viewModel: BouncerViewModel, + dialogFactory: BouncerDialogFactory, +) { + val dialogMessage: String? by viewModel.dialogMessage.collectAsState() + var dialog: Dialog? by remember { mutableStateOf(null) } + + if (dialogMessage != null) { + if (dialog == null) { + dialog = + dialogFactory().apply { + setMessage(dialogMessage) + setButton( + DialogInterface.BUTTON_NEUTRAL, + context.getString(R.string.ok), + ) { _, _ -> + viewModel.onDialogDismissed() + } + setCancelable(false) + setCanceledOnTouchOutside(false) + show() + } + } + } else { + dialog?.dismiss() + dialog = null } } @@ -420,6 +704,14 @@ private fun UserSwitcher( viewModel: BouncerViewModel, modifier: Modifier = Modifier, ) { + if (!viewModel.isUserSwitcherVisible) { + // Take up the same space as the user switcher normally would, but with nothing inside it. + Box( + modifier = modifier, + ) + return + } + val selectedUserImage by viewModel.selectedUserImage.collectAsState(null) val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList()) @@ -539,195 +831,10 @@ private fun UserSwitcherDropdownMenu( } } -/** - * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable - * by double-tapping on the side. - */ -@Composable -private fun SplitLayout( - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - modifier: Modifier = Modifier, -) { - SwappableLayout( - startContent = { startContentModifier -> - StandardLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - layout = BouncerSceneLayout.SPLIT, - outputOnly = true, - modifier = startContentModifier, - ) - }, - endContent = { endContentModifier -> - UserInputArea( - viewModel = viewModel, - visibility = UserInputAreaVisibility.INPUT_ONLY, - layout = BouncerSceneLayout.SPLIT, - modifier = endContentModifier, - ) - }, - layout = BouncerSceneLayout.SPLIT, - modifier = modifier, - ) -} - -/** - * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background - * to flip their positions. - */ -@Composable -private fun SwappableLayout( - startContent: @Composable (Modifier) -> Unit, - endContent: @Composable (Modifier) -> Unit, - layout: BouncerSceneLayout, - modifier: Modifier = Modifier, -) { - val layoutDirection = LocalLayoutDirection.current - val isLeftToRight = layoutDirection == LayoutDirection.Ltr - val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) } - - Row( - modifier = - modifier.pointerInput(Unit) { - detectTapGestures( - onDoubleTap = { offset -> - // Depending on where the user double tapped, switch the elements such that - // the endContent is closer to the side that was double tapped. - setSwapped(offset.x < size.width / 2) - } - ) - }, - ) { - val animatedOffset by - animateFloatAsState( - targetValue = - if (!isSwapped) { - // When startContent is first, both elements have their natural placement so - // they are not offset in any way. - 0f - } else if (isLeftToRight) { - // Since startContent is not first, the elements have to be swapped - // horizontally. In the case of LTR locales, this means pushing startContent - // to the right, hence the positive number. - 1f - } else { - // Since startContent is not first, the elements have to be swapped - // horizontally. In the case of RTL locales, this means pushing startContent - // to the left, hence the negative number. - -1f - }, - label = "offset", - ) - - startContent( - Modifier.fillMaxHeight().weight(1f).graphicsLayer { - translationX = size.width * animatedOffset - alpha = animatedAlpha(animatedOffset) - } - ) - - Box( - modifier = - Modifier.fillMaxHeight().weight(1f).graphicsLayer { - // A negative sign is used to make sure this is offset in the direction that's - // opposite of the direction that the user switcher is pushed in. - translationX = -size.width * animatedOffset - alpha = animatedAlpha(animatedOffset) - } - ) { - endContent(Modifier.align(layout.swappableEndContentAlignment).widthIn(max = 400.dp)) - } - } -} - -/** - * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap - * anywhere on the background to flip their positions. - * - * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the - * UI for the bouncer will be shown on its own, taking up one side, with the other side just being - * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard - * rendering of the bouncer will be used instead of the side-by-side layout. - */ -@Composable -private fun SideBySideLayout( - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - isUserSwitcherVisible: Boolean, - modifier: Modifier = Modifier, -) { - SwappableLayout( - startContent = { startContentModifier -> - if (isUserSwitcherVisible) { - UserSwitcher( - viewModel = viewModel, - modifier = startContentModifier, - ) - } else { - Box( - modifier = startContentModifier, - ) - } - }, - endContent = { endContentModifier -> - StandardLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - layout = BouncerSceneLayout.SIDE_BY_SIDE, - modifier = endContentModifier, - ) - }, - layout = BouncerSceneLayout.SIDE_BY_SIDE, - modifier = modifier, - ) -} - -/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */ -@Composable -private fun StackedLayout( - viewModel: BouncerViewModel, - dialogFactory: BouncerDialogFactory, - isUserSwitcherVisible: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - ) { - if (isUserSwitcherVisible) { - UserSwitcher( - viewModel = viewModel, - modifier = Modifier.fillMaxWidth().weight(1f), - ) - } - - StandardLayout( - viewModel = viewModel, - dialogFactory = dialogFactory, - layout = BouncerSceneLayout.STACKED, - modifier = Modifier.fillMaxWidth().weight(1f), - ) - } -} - interface BouncerDialogFactory { operator fun invoke(): AlertDialog } -/** Enumerates all supported user-input area visibilities. */ -private enum class UserInputAreaVisibility { - /** - * Only the area where the user enters the input is shown; the area where the input is reflected - * back to the user is not shown. - */ - INPUT_ONLY, - /** - * Only the area where the input is reflected back to the user is shown; the area where the - * input is entered by the user is not shown. - */ - OUTPUT_ONLY, -} - /** * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of * the two reaches a stopping point but `0` in the middle of the transition. @@ -774,48 +881,3 @@ private object SceneElements { private val SceneTransitions = transitions { from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() } } - -/** Whether a more compact size should be used for various spacing dimensions. */ -internal val BouncerSceneLayout.isUseCompactSize: Boolean - get() = - when (this) { - BouncerSceneLayout.SIDE_BY_SIDE -> true - BouncerSceneLayout.SPLIT -> true - else -> false - } - -/** Amount of space to place between the message and the entered input UI elements, in dips. */ -private val BouncerSceneLayout.spacingBetweenMessageAndEnteredInput: Dp - get() = - when { - this == BouncerSceneLayout.STACKED -> 24.dp - isUseCompactSize -> 96.dp - else -> 128.dp - } - -/** Amount of space to place above the topmost UI element, in dips. */ -private val BouncerSceneLayout.topPadding: Dp - get() = - if (this == BouncerSceneLayout.SPLIT) { - 40.dp - } else { - 92.dp - } - -/** Amount of space to place below the bottommost UI element, in dips. */ -private val BouncerSceneLayout.bottomPadding: Dp - get() = - if (this == BouncerSceneLayout.SPLIT) { - 40.dp - } else { - 48.dp - } - -/** The in-a-box alignment for the content on the "end" side of a swappable layout. */ -private val BouncerSceneLayout.swappableEndContentAlignment: Alignment - get() = - if (this == BouncerSceneLayout.SPLIT) { - Alignment.Center - } else { - Alignment.BottomCenter - } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt index 08b7559dcf97..1c3d93c3b7e9 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt @@ -26,8 +26,8 @@ import com.android.systemui.bouncer.ui.helper.calculateLayoutInternal /** * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If - * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.SIDE_BY_SIDE] is replaced by - * [BouncerSceneLayout.STANDARD]. + * [isSideBySideSupported] is `false`, then [BouncerSceneLayout.BESIDE_USER_SWITCHER] is replaced by + * [BouncerSceneLayout.STANDARD_BOUNCER]. */ @Composable fun calculateLayout( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt index 279995959da2..09608115c456 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt @@ -17,7 +17,6 @@ package com.android.systemui.bouncer.ui.composable import android.view.ViewTreeObserver -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.LocalTextStyle @@ -31,7 +30,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.FocusRequester @@ -81,42 +79,38 @@ internal fun PasswordBouncer( } } - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier, - ) { - val color = MaterialTheme.colorScheme.onSurfaceVariant - val lineWidthPx = with(LocalDensity.current) { 2.dp.toPx() } + val color = MaterialTheme.colorScheme.onSurfaceVariant + val lineWidthPx = with(LocalDensity.current) { 2.dp.toPx() } - TextField( - value = password, - onValueChange = viewModel::onPasswordInputChanged, - enabled = isInputEnabled, - visualTransformation = PasswordVisualTransformation(), - singleLine = true, - textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center), - keyboardOptions = - KeyboardOptions( - keyboardType = KeyboardType.Password, - imeAction = ImeAction.Done, - ), - keyboardActions = - KeyboardActions( - onDone = { viewModel.onAuthenticateKeyPressed() }, - ), - modifier = - Modifier.focusRequester(focusRequester) - .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) } - .drawBehind { - drawLine( - color = color, - start = Offset(x = 0f, y = size.height - lineWidthPx), - end = Offset(size.width, y = size.height - lineWidthPx), - strokeWidth = lineWidthPx, - ) - }, - ) - } + TextField( + value = password, + onValueChange = viewModel::onPasswordInputChanged, + enabled = isInputEnabled, + visualTransformation = PasswordVisualTransformation(), + singleLine = true, + textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center), + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = + KeyboardActions( + onDone = { viewModel.onAuthenticateKeyPressed() }, + ), + modifier = + modifier + .focusRequester(focusRequester) + .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) } + .drawBehind { + drawLine( + color = color, + start = Offset(x = 0f, y = size.height - lineWidthPx), + end = Offset(size.width, y = size.height - lineWidthPx), + strokeWidth = lineWidthPx, + ) + }, + ) } /** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */ diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt index a4b195508b3d..0a5f5d281f83 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt @@ -24,6 +24,8 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -48,7 +50,6 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.compose.modifiers.thenIf import com.android.internal.R -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel import kotlin.math.min @@ -61,11 +62,14 @@ import kotlinx.coroutines.launch * UI for the input part of a pattern-requiring version of the bouncer. * * The user can press, hold, and drag their pointer to select dots along a grid of dots. + * + * If [centerDotsVertically] is `true`, the dots should be centered along the axis of interest; if + * `false`, the dots will be pushed towards the end/bottom of the axis. */ @Composable internal fun PatternBouncer( viewModel: PatternBouncerViewModel, - layout: BouncerSceneLayout, + centerDotsVertically: Boolean, modifier: Modifier = Modifier, ) { DisposableEffect(Unit) { @@ -197,6 +201,14 @@ internal fun PatternBouncer( Canvas( modifier + // Because the width also includes spacing to the left and right of the leftmost and + // rightmost dots in the grid and because UX mocks specify the width without that + // spacing, the actual width needs to be defined slightly bigger than the UX mock width. + .width((262 * colCount / 2).dp) + // Because the height also includes spacing above and below the topmost and bottommost + // dots in the grid and because UX mocks specify the height without that spacing, the + // actual height needs to be defined slightly bigger than the UX mock height. + .height((262 * rowCount / 2).dp) // Need to clip to bounds to make sure that the lines don't follow the input pointer // when it leaves the bounds of the dot grid. .clipToBounds() @@ -260,7 +272,7 @@ internal fun PatternBouncer( availableSize = containerSize.height, spacingPerDot = spacing, dotCount = rowCount, - isCentered = layout.isCenteredVertically, + isCentered = centerDotsVertically, ) offset = Offset(horizontalOffset, verticalOffset) scale = (colCount * spacing) / containerSize.width @@ -423,10 +435,6 @@ private fun offset( } } -/** Whether the UI should be centered vertically. */ -private val BouncerSceneLayout.isCenteredVertically: Boolean - get() = this == BouncerSceneLayout.SPLIT - private const val DOT_DIAMETER_DP = 16 private const val SELECTED_DOT_DIAMETER_DP = 24 private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83 diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt index 8f5d9f4a1790..f505b9067140 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt @@ -52,7 +52,6 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.compose.grid.VerticalGrid import com.android.compose.modifiers.thenIf -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.common.shared.model.ContentDescription @@ -70,7 +69,7 @@ import kotlinx.coroutines.launch @Composable fun PinPad( viewModel: PinBouncerViewModel, - layout: BouncerSceneLayout, + verticalSpacing: Dp, modifier: Modifier = Modifier, ) { DisposableEffect(Unit) { @@ -96,8 +95,8 @@ fun PinPad( VerticalGrid( columns = columns, - verticalSpacing = layout.verticalSpacing, - horizontalSpacing = calculateHorizontalSpacingBetweenColumns(layout.gridWidth), + verticalSpacing = verticalSpacing, + horizontalSpacing = calculateHorizontalSpacingBetweenColumns(gridWidth = 300.dp), modifier = modifier, ) { repeat(9) { index -> @@ -355,14 +354,6 @@ private fun calculateHorizontalSpacingBetweenColumns( return (gridWidth - (pinButtonMaxSize * columns)) / (columns - 1) } -/** The width of the grid of PIN pad buttons, in dips. */ -private val BouncerSceneLayout.gridWidth: Dp - get() = if (isUseCompactSize) 292.dp else 300.dp - -/** The spacing between rows of PIN pad buttons, in dips. */ -private val BouncerSceneLayout.verticalSpacing: Dp - get() = if (isUseCompactSize) 8.dp else 12.dp - /** Number of columns in the PIN pad grid. */ private const val columns = 3 /** Maximum size (width and height) of each PIN pad button. */ diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index c49c19785624..12f1b301c836 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -27,11 +27,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onPlaced @@ -89,13 +91,18 @@ fun SceneScope.NotificationStack( isScrimVisible: Boolean, modifier: Modifier = Modifier, ) { + val cornerRadius by viewModel.cornerRadiusDp.collectAsState() + Box(modifier = modifier) { if (isScrimVisible) { Box( modifier = Modifier.element(Notifications.Elements.NotificationScrim) .fillMaxSize() - .clip(RoundedCornerShape(32.dp)) + .graphicsLayer { + shape = RoundedCornerShape(cornerRadius.dp) + clip = true + } .background(MaterialTheme.colorScheme.surface) ) } @@ -167,7 +174,9 @@ private fun SceneScope.NotificationPlaceholder( } val boundsInWindow = coordinates.boundsInWindow() viewModel.onBoundsChanged( + left = boundsInWindow.left, top = boundsInWindow.top, + right = boundsInWindow.right, bottom = boundsInWindow.bottom, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt index 5d8eaf7f3d15..58052cd60f39 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt @@ -2,10 +2,9 @@ package com.android.compose.animation.scene import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.unit.IntSize interface DraggableHandler { - fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int = 1) + fun onDragStarted(startedPosition: Offset, overSlop: Float, pointersDown: Int = 1) fun onDelta(pixels: Float) fun onDragStopped(velocity: Float) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index a0fba8076517..38738782c889 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -66,8 +66,8 @@ internal fun Modifier.multiPointerDraggable( orientation: Orientation, enabled: Boolean, startDragImmediately: Boolean, - onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit, - onDragDelta: (Float) -> Unit, + onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, + onDragDelta: (delta: Float) -> Unit, onDragStopped: (velocity: Float) -> Unit, ): Modifier = this.then( @@ -86,7 +86,7 @@ private data class MultiPointerDraggableElement( private val enabled: Boolean, private val startDragImmediately: Boolean, private val onDragStarted: - (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit, + (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, private val onDragDelta: (Float) -> Unit, private val onDragStopped: (velocity: Float) -> Unit, ) : ModifierNodeElement<MultiPointerDraggableNode>() { @@ -114,7 +114,7 @@ private class MultiPointerDraggableNode( orientation: Orientation, enabled: Boolean, var startDragImmediately: Boolean, - var onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit, + var onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, var onDragDelta: (Float) -> Unit, var onDragStopped: (velocity: Float) -> Unit, ) : PointerInputModifierNode, DelegatingNode(), CompositionLocalConsumerModifierNode { @@ -153,9 +153,9 @@ private class MultiPointerDraggableNode( return } - val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown -> + val onDragStart: (Offset, Float, Int) -> Unit = { startedPosition, overSlop, pointersDown -> velocityTracker.resetTracking() - onDragStarted(size, startedPosition, pointersDown) + onDragStarted(startedPosition, overSlop, pointersDown) } val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) } @@ -203,7 +203,7 @@ private class MultiPointerDraggableNode( private suspend fun PointerInputScope.detectDragGestures( orientation: Orientation, startDragImmediately: () -> Boolean, - onDragStart: (startedPosition: Offset, pointersDown: Int) -> Unit, + onDragStart: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, onDragEnd: () -> Unit, onDragCancel: () -> Unit, onDrag: (change: PointerInputChange, dragAmount: Float) -> Unit, @@ -241,7 +241,7 @@ private suspend fun PointerInputScope.detectDragGestures( } } - onDragStart(drag.position, pressed.size) + onDragStart(drag.position, overSlop, pressed.size) onDrag(drag, overSlop) val successful = diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index 2986504b7dbc..ded6cc155b0b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -44,22 +44,22 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the * gesture begins at the edge of the scrollable component (so that a scroll in that direction * can no longer be consumed). If the gesture is partially consumed by the scrollable component, - * there will be NO overscroll effect between scenes. + * there will be NO preview of the next scene. * * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - EdgeNoOverscroll(canStartOnPostFling = false), + EdgeNoPreview(canStartOnPostFling = false), /** * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the * gesture begins at the edge of the scrollable component. If the gesture is partially consumed - * by the scrollable component, there will be an overscroll effect between scenes. + * by the scrollable component, there will be a preview of the next scene. * * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - EdgeWithOverscroll(canStartOnPostFling = true), + EdgeWithPreview(canStartOnPostFling = true), /** * Any overscroll will be used by the [SceneTransitionLayout] to move to the next scene. @@ -67,7 +67,7 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { * In addition, during scene transitions, scroll events are consumed by the * [SceneTransitionLayout] instead of the scrollable component. */ - Always(canStartOnPostFling = true), + EdgeAlways(canStartOnPostFling = true), } internal fun Modifier.nestedScrollToScene( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index b00c88612269..212c9eb65138 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:Suppress("NOTHING_TO_INLINE") + package com.android.compose.animation.scene import android.util.Log @@ -26,7 +28,6 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import com.android.compose.nestedscroll.PriorityNestedScrollConnection @@ -48,14 +49,13 @@ internal class SceneGestureHandler( layoutImpl.state.transitionState = value } - /** - * The transition controlled by this gesture handler. It will be set as the [transitionState] in - * the [SceneTransitionLayoutImpl] whenever this handler is driving the current transition. - * - * Note: the initialScene here does not matter, it's only used for initializing the transition - * and will be replaced when a drag event starts. - */ - internal val swipeTransition = SwipeTransition(initialScene = currentScene) + internal var swipeTransition: SwipeTransition = SwipeTransition(currentScene, currentScene, 1f) + private set + + private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { + if (isDrivingTransition || force) transitionState = newTransition + swipeTransition = newTransition + } internal val currentScene: Scene get() = layoutImpl.scene(transitionState.currentScene) @@ -63,15 +63,6 @@ internal class SceneGestureHandler( internal val isDrivingTransition get() = transitionState == swipeTransition - internal var isAnimatingOffset - get() = swipeTransition.isAnimatingOffset - private set(value) { - swipeTransition.isAnimatingOffset = value - } - - internal val swipeTransitionToScene - get() = swipeTransition._toScene - /** * The velocity threshold at which the intent of the user is to swipe up or down. It is the same * as SwipeableV2Defaults.VelocityThreshold. @@ -86,11 +77,17 @@ internal class SceneGestureHandler( internal var gestureWithPriority: Any? = null - internal fun onDragStarted(pointersDown: Int, layoutSize: IntSize, startedPosition: Offset?) { + /** The [UserAction]s associated to the current swipe. */ + private var actionUpOrLeft: UserAction? = null + private var actionDownOrRight: UserAction? = null + private var actionUpOrLeftNoEdge: UserAction? = null + private var actionDownOrRightNoEdge: UserAction? = null + + internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. // Stop animating and start from where the current offset. - swipeTransition.stopOffsetAnimation() + swipeTransition.cancelOffsetAnimation() return } @@ -106,37 +103,29 @@ internal class SceneGestureHandler( } val fromScene = currentScene + setCurrentActions(fromScene, startedPosition, pointersDown) - swipeTransition._currentScene = fromScene - swipeTransition._fromScene = fromScene - - // We don't know where we are transitioning to yet given that the drag just started, so set - // it to fromScene, which will effectively be treated the same as Idle(fromScene). - swipeTransition._toScene = fromScene + if (fromScene.upOrLeft() == null && fromScene.downOrRight() == null) { + return + } - swipeTransition.stopOffsetAnimation() - swipeTransition.dragOffset = 0f + val (targetScene, distance) = fromScene.findTargetSceneAndDistance(overSlop) - // Use the layout size in the swipe orientation for swipe distance. - // TODO(b/290184746): Also handle custom distances for transitions. With smaller distances, - // we will also have to make sure that we correctly handle overscroll. - swipeTransition.absoluteDistance = - when (orientation) { - Orientation.Horizontal -> layoutSize.width - Orientation.Vertical -> layoutSize.height - }.toFloat() + updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true) + } + private fun setCurrentActions(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) { val fromEdge = startedPosition?.let { position -> layoutImpl.edgeDetector.edge( - layoutSize, + fromScene.targetSize, position.round(), layoutImpl.density, orientation, ) } - swipeTransition.actionUpOrLeft = + val upOrLeft = Swipe( direction = when (orientation) { @@ -147,7 +136,7 @@ internal class SceneGestureHandler( fromEdge = fromEdge, ) - swipeTransition.actionDownOrRight = + val downOrRight = Swipe( direction = when (orientation) { @@ -159,108 +148,114 @@ internal class SceneGestureHandler( ) if (fromEdge == null) { - swipeTransition.actionUpOrLeftNoEdge = null - swipeTransition.actionDownOrRightNoEdge = null + actionUpOrLeft = null + actionDownOrRight = null + actionUpOrLeftNoEdge = upOrLeft + actionDownOrRightNoEdge = downOrRight } else { - swipeTransition.actionUpOrLeftNoEdge = - (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null) - swipeTransition.actionDownOrRightNoEdge = - (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null) + actionUpOrLeft = upOrLeft + actionDownOrRight = downOrRight + actionUpOrLeftNoEdge = upOrLeft.copy(fromEdge = null) + actionDownOrRightNoEdge = downOrRight.copy(fromEdge = null) } + } - if (swipeTransition.absoluteDistance > 0f) { - transitionState = swipeTransition - } + /** + * Use the layout size in the swipe orientation for swipe distance. + * + * TODO(b/290184746): Also handle custom distances for transitions. With smaller distances, we + * will also have to make sure that we correctly handle overscroll. + */ + private fun Scene.getAbsoluteDistance(): Float { + return when (orientation) { + Orientation.Horizontal -> targetSize.width + Orientation.Vertical -> targetSize.height + }.toFloat() } internal fun onDrag(delta: Float) { - if (delta == 0f) return - + if (delta == 0f || !isDrivingTransition) return swipeTransition.dragOffset += delta - // First check transition.fromScene should be changed for the case where the user quickly - // swiped twice in a row to accelerate the transition and go from A => B then B => C really - // fast. - maybeHandleAcceleratedSwipe() - - val offset = swipeTransition.dragOffset - val fromScene = swipeTransition._fromScene + val (fromScene, acceleratedOffset) = + computeFromSceneConsideringAcceleratedSwipe(swipeTransition) + swipeTransition.dragOffset += acceleratedOffset // Compute the target scene depending on the current offset. - val target = fromScene.findTargetSceneAndDistance(offset) + val (targetScene, distance) = + fromScene.findTargetSceneAndDistance(swipeTransition.dragOffset) - if (swipeTransition._toScene.key != target.sceneKey) { - swipeTransition._toScene = layoutImpl.scenes.getValue(target.sceneKey) - } - - if (swipeTransition._distance != target.distance) { - swipeTransition._distance = target.distance + // TODO(b/290184746): support long scroll A => B => C? especially for non fullscreen scenes + if ( + fromScene.key != swipeTransition.fromScene || targetScene.key != swipeTransition.toScene + ) { + updateTransition( + SwipeTransition(fromScene, targetScene, distance).apply { + this.dragOffset = swipeTransition.dragOffset + } + ) } } /** * Change fromScene in the case where the user quickly swiped multiple times in the same * direction to accelerate the transition from A => B then B => C. + * + * @return the new fromScene and a dragOffset to be added in case the scene has changed + * + * TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging twice + * before B has been reached */ - private fun maybeHandleAcceleratedSwipe() { + private inline fun computeFromSceneConsideringAcceleratedSwipe( + swipeTransition: SwipeTransition, + ): Pair<Scene, Float> { val toScene = swipeTransition._toScene val fromScene = swipeTransition._fromScene + val absoluteDistance = swipeTransition.distance.absoluteValue // If the swipe was not committed, don't do anything. if (fromScene == toScene || swipeTransition._currentScene != toScene) { - return + return Pair(fromScene, 0f) } // If the offset is past the distance then let's change fromScene so that the user can swipe // to the next screen or go back to the previous one. val offset = swipeTransition.dragOffset - val absoluteDistance = swipeTransition.absoluteDistance - if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) { - swipeTransition.dragOffset += absoluteDistance - swipeTransition._fromScene = toScene - } else if ( - offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key - ) { - swipeTransition.dragOffset -= absoluteDistance - swipeTransition._fromScene = toScene + return if (offset <= -absoluteDistance && fromScene.upOrLeft() == toScene.key) { + Pair(toScene, absoluteDistance) + } else if (offset >= absoluteDistance && fromScene.downOrRight() == toScene.key) { + Pair(toScene, -absoluteDistance) + } else { + Pair(fromScene, 0f) } - - // Important note: toScene and distance will be updated right after this function is called, - // using fromScene and dragOffset. } - private class TargetScene( - val sceneKey: SceneKey, - val distance: Float, - ) - - private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene { - val upOrLeft = swipeTransition.upOrLeft(this) - val downOrRight = swipeTransition.downOrRight(this) + // TODO(b/290184746): there are two bugs here: + // 1. if both upOrLeft and downOrRight become `null` during a transition this will crash + // 2. if one of them changes during a transition, the transition will jump cut to the new target + private inline fun Scene.findTargetSceneAndDistance( + directionOffset: Float + ): Pair<Scene, Float> { + val upOrLeft = upOrLeft() + val downOrRight = downOrRight() + val absoluteDistance = getAbsoluteDistance() // Compute the target scene depending on the current offset. - return when { - directionOffset < 0f && upOrLeft != null -> { - TargetScene( - sceneKey = upOrLeft, - distance = -swipeTransition.absoluteDistance, - ) - } - directionOffset > 0f && downOrRight != null -> { - TargetScene( - sceneKey = downOrRight, - distance = swipeTransition.absoluteDistance, - ) - } - else -> { - TargetScene( - sceneKey = key, - distance = 0f, - ) - } + return if ((directionOffset < 0f && upOrLeft != null) || downOrRight == null) { + Pair(layoutImpl.scene(upOrLeft!!), -absoluteDistance) + } else { + Pair(layoutImpl.scene(downOrRight), absoluteDistance) } } + private fun Scene.upOrLeft(): SceneKey? { + return userActions[actionUpOrLeft] ?: userActions[actionUpOrLeftNoEdge] + } + + private fun Scene.downOrRight(): SceneKey? { + return userActions[actionDownOrRight] ?: userActions[actionDownOrRightNoEdge] + } + internal fun onDragStopped(velocity: Float, canChangeScene: Boolean) { // The state was changed since the drag started; don't do anything. if (!isDrivingTransition) { @@ -291,11 +286,6 @@ internal class SceneGestureHandler( // velocity and offset of the transition, then we launch the animation. val toScene = swipeTransition._toScene - if (fromScene == toScene) { - // We were not animating. - transitionState = TransitionState.Idle(fromScene.key) - return - } // Compute the destination scene (and therefore offset) to settle in. val offset = swipeTransition.dragOffset @@ -322,12 +312,14 @@ internal class SceneGestureHandler( if (startFromIdlePosition) { // If there is a next scene, we start the overscroll animation. - val target = fromScene.findTargetSceneAndDistance(velocity) - val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key + val (targetScene, distance) = fromScene.findTargetSceneAndDistance(velocity) + val isValidTarget = distance != 0f && targetScene.key != fromScene.key if (isValidTarget) { - swipeTransition._toScene = layoutImpl.scene(target.sceneKey) - swipeTransition._distance = target.distance - + updateTransition( + SwipeTransition(fromScene, targetScene, distance).apply { + _currentScene = swipeTransition._currentScene + } + ) animateTo(targetScene = fromScene, targetOffset = 0f) } else { // We will not animate @@ -382,10 +374,10 @@ internal class SceneGestureHandler( ) { swipeTransition.startOffsetAnimation { coroutineScope.launch { - if (!isAnimatingOffset) { + if (!swipeTransition.isAnimatingOffset) { swipeTransition.offsetAnimatable.snapTo(swipeTransition.dragOffset) } - isAnimatingOffset = true + swipeTransition.isAnimatingOffset = true swipeTransition.offsetAnimatable.animateTo( targetOffset, @@ -397,7 +389,7 @@ internal class SceneGestureHandler( initialVelocity = initialVelocity, ) - isAnimatingOffset = false + swipeTransition.finishOffsetAnimation() // Now that the animation is done, the state should be idle. Note that if the state // was changed since this animation started, some external code changed it and we @@ -410,29 +402,26 @@ internal class SceneGestureHandler( } } - internal class SwipeTransition(initialScene: Scene) : TransitionState.Transition { - var _currentScene by mutableStateOf(initialScene) + internal class SwipeTransition( + val _fromScene: Scene, + val _toScene: Scene, + /** + * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is + * above or to the left of [toScene]. + */ + val distance: Float + ) : TransitionState.Transition { + var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey get() = _currentScene.key - var _fromScene by mutableStateOf(initialScene) - override val fromScene: SceneKey - get() = _fromScene.key + override val fromScene: SceneKey = _fromScene.key - var _toScene by mutableStateOf(initialScene) - override val toScene: SceneKey - get() = _toScene.key + override val toScene: SceneKey = _toScene.key override val progress: Float get() { val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset - if (distance == 0f) { - // This can happen only if fromScene == toScene. - error( - "Transition.progress should be called only when Transition.fromScene != " + - "Transition.toScene" - ) - } return offset / distance } @@ -459,46 +448,22 @@ internal class SceneGestureHandler( /** Ends any previous [offsetAnimationJob] and runs the new [job]. */ fun startOffsetAnimation(job: () -> Job) { - stopOffsetAnimation() + cancelOffsetAnimation() offsetAnimationJob = job() } - /** Stops any ongoing offset animation. */ - fun stopOffsetAnimation() { + /** Cancel any ongoing offset animation. */ + fun cancelOffsetAnimation() { offsetAnimationJob?.cancel() + finishOffsetAnimation() + } + fun finishOffsetAnimation() { if (isAnimatingOffset) { isAnimatingOffset = false dragOffset = offsetAnimatable.value } } - - /** The absolute distance between [fromScene] and [toScene]. */ - var absoluteDistance = 0f - - /** - * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is - * above or to the left of [toScene]. - */ - var _distance by mutableFloatStateOf(0f) - val distance: Float - get() = _distance - - /** The [UserAction]s associated to this swipe. */ - var actionUpOrLeft: UserAction = Back - var actionDownOrRight: UserAction = Back - var actionUpOrLeftNoEdge: UserAction? = null - var actionDownOrRightNoEdge: UserAction? = null - - fun upOrLeft(scene: Scene): SceneKey? { - return scene.userActions[actionUpOrLeft] - ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] } - } - - fun downOrRight(scene: Scene): SceneKey? { - return scene.userActions[actionDownOrRight] - ?: actionDownOrRightNoEdge?.let { scene.userActions[it] } - } } companion object { @@ -509,9 +474,9 @@ internal class SceneGestureHandler( private class SceneDraggableHandler( private val gestureHandler: SceneGestureHandler, ) : DraggableHandler { - override fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) { + override fun onDragStarted(startedPosition: Offset, overSlop: Float, pointersDown: Int) { gestureHandler.gestureWithPriority = this - gestureHandler.onDragStarted(pointersDown, layoutSize, startedPosition) + gestureHandler.onDragStarted(pointersDown, startedPosition, overSlop) } override fun onDelta(pixels: Float) { @@ -589,7 +554,7 @@ internal class SceneNestedScrollHandler( // The progress value can go beyond this range in the case of overscroll. val shouldSnapToIdle = isProgressCloseTo(0f) || isProgressCloseTo(1f) if (shouldSnapToIdle) { - gestureHandler.swipeTransition.stopOffsetAnimation() + gestureHandler.swipeTransition.cancelOffsetAnimation() gestureHandler.transitionState = TransitionState.Idle(gestureHandler.swipeTransition.currentScene) } @@ -612,15 +577,15 @@ internal class SceneNestedScrollHandler( canChangeScene = false // unused: added for consistency false } - NestedScrollBehavior.EdgeNoOverscroll -> { + NestedScrollBehavior.EdgeNoPreview -> { canChangeScene = isZeroOffset isZeroOffset && hasNextScene(offsetAvailable) } - NestedScrollBehavior.EdgeWithOverscroll -> { + NestedScrollBehavior.EdgeWithPreview -> { canChangeScene = isZeroOffset hasNextScene(offsetAvailable) } - NestedScrollBehavior.Always -> { + NestedScrollBehavior.EdgeAlways -> { canChangeScene = true hasNextScene(offsetAvailable) } @@ -639,12 +604,12 @@ internal class SceneNestedScrollHandler( behavior.canStartOnPostFling && hasNextScene(velocityAvailable) }, canContinueScroll = { true }, - onStart = { + onStart = { offsetAvailable -> gestureHandler.gestureWithPriority = this gestureHandler.onDragStarted( pointersDown = 1, - layoutSize = gestureHandler.currentScene.targetSize, startedPosition = null, + overSlop = offsetAvailable, ) }, onScroll = { offsetAvailable -> diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 07add77eccd4..afa184b15901 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -132,8 +132,8 @@ interface SceneScope { */ fun Modifier.nestedScrollToScene( orientation: Orientation, - startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoOverscroll, - endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoOverscroll, + startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, + endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, ): Modifier /** diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 2c78dee56bbc..116a66673d0a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -46,7 +46,7 @@ internal fun Modifier.swipeToScene(gestureHandler: SceneGestureHandler): Modifie // user can't swipe in the other direction. startDragImmediately = gestureHandler.isDrivingTransition && - gestureHandler.isAnimatingOffset && + gestureHandler.swipeTransition.isAnimatingOffset && !canOppositeSwipe, onDragStarted = gestureHandler.draggable::onDragStarted, onDragDelta = gestureHandler.draggable::onDelta, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index a5fd1bfb72e6..c49a2b8bbe32 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -38,7 +38,7 @@ class PriorityNestedScrollConnection( private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean, private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean, private val canContinueScroll: () -> Boolean, - private val onStart: () -> Unit, + private val onStart: (offsetAvailable: Offset) -> Unit, private val onScroll: (offsetAvailable: Offset) -> Offset, private val onStop: (velocityAvailable: Velocity) -> Velocity, ) : NestedScrollConnection { @@ -131,7 +131,7 @@ class PriorityNestedScrollConnection( // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is // lifted (step 3b), or this object has been destroyed (step 3c). - onStart() + onStart(available) return onScroll(available) } @@ -156,7 +156,7 @@ fun PriorityNestedScrollConnection( canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, canStartPostFling: (velocityAvailable: Float) -> Boolean, canContinueScroll: () -> Boolean, - onStart: () -> Unit, + onStart: (offsetAvailable: Float) -> Unit, onScroll: (offsetAvailable: Float) -> Float, onStop: (velocityAvailable: Float) -> Float, ) = @@ -172,7 +172,7 @@ fun PriorityNestedScrollConnection( canStartPostFling(velocityAvailable.toFloat()) }, canContinueScroll = canContinueScroll, - onStart = onStart, + onStart = { offsetAvailable -> onStart(offsetAvailable.toFloat()) }, onScroll = { offsetAvailable: Offset -> onScroll(offsetAvailable.toFloat()).toOffset() }, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index 49ef31b16d73..aa942e039856 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -29,10 +29,10 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.compose.animation.scene.NestedScrollBehavior.Always import com.android.compose.animation.scene.NestedScrollBehavior.DuringTransitionBetweenScenes -import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoOverscroll -import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithOverscroll +import com.android.compose.animation.scene.NestedScrollBehavior.EdgeAlways +import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoPreview +import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.TestScenes.SceneC @@ -65,30 +65,53 @@ class SceneGestureHandlerTest { ) { Text("SceneA") } - scene(SceneB) { Text("SceneB") } - scene(SceneC) { Text("SceneC") } + scene( + key = SceneB, + userActions = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA), + ) { + Text("SceneB") + } + scene( + key = SceneC, + userActions = + mapOf( + Swipe.Up to SceneB, + Swipe(SwipeDirection.Up, fromEdge = Edge.Bottom) to SceneA + ), + ) { + Text("SceneC") + } } val transitionInterceptionThreshold = 0.05f + private val layoutImpl = + SceneTransitionLayoutImpl( + onChangeScene = { internalCurrentScene = it }, + builder = scenesBuilder, + transitions = EmptyTestTransitions, + state = layoutState, + density = Density(1f), + edgeDetector = DefaultEdgeDetector, + transitionInterceptionThreshold = transitionInterceptionThreshold, + coroutineScope = coroutineScope, + ) + .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) } + val sceneGestureHandler = SceneGestureHandler( - layoutImpl = - SceneTransitionLayoutImpl( - onChangeScene = { internalCurrentScene = it }, - builder = scenesBuilder, - transitions = EmptyTestTransitions, - state = layoutState, - density = Density(1f), - edgeDetector = DefaultEdgeDetector, - transitionInterceptionThreshold = transitionInterceptionThreshold, - coroutineScope = coroutineScope, - ) - .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) }, + layoutImpl = layoutImpl, orientation = Orientation.Vertical, coroutineScope = coroutineScope, ) + val horizontalSceneGestureHandler = + SceneGestureHandler( + layoutImpl = layoutImpl, + orientation = Orientation.Horizontal, + coroutineScope = coroutineScope, + ) + val draggable = sceneGestureHandler.draggable fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) = @@ -101,11 +124,17 @@ class SceneGestureHandlerTest { val velocityThreshold = sceneGestureHandler.velocityThreshold - // 10% of the screen - val deltaInPixels10 = SCREEN_SIZE * 0.1f + fun down(fractionOfScreen: Float) = + if (fractionOfScreen < 0f) error("use up()") else SCREEN_SIZE * fractionOfScreen + + fun up(fractionOfScreen: Float) = + if (fractionOfScreen < 0f) error("use down()") else -down(fractionOfScreen) + + // Float tolerance for comparisons + val tolerance = 0.00001f // Offset y: 10% of the screen - val offsetY10 = Offset(x = 0f, y = deltaInPixels10) + val offsetY10 = Offset(x = 0f, y = down(0.1f)) val transitionState: TransitionState get() = layoutState.transitionState @@ -121,12 +150,39 @@ class SceneGestureHandlerTest { coroutineScope.testScheduler.runCurrent() } - fun assertScene(currentScene: SceneKey, isIdle: Boolean) { - val idleMsg = if (isIdle) "MUST" else "MUST NOT" - assertWithMessage("transitionState $idleMsg be Idle") - .that(transitionState is Idle) - .isEqualTo(isIdle) - assertThat(transitionState.currentScene).isEqualTo(currentScene) + fun assertIdle(currentScene: SceneKey) { + assertWithMessage("transitionState must be Idle").that(transitionState is Idle).isTrue() + assertWithMessage("currentScene does not match") + .that(transitionState.currentScene) + .isEqualTo(currentScene) + } + + fun assertTransition( + currentScene: SceneKey? = null, + fromScene: SceneKey? = null, + toScene: SceneKey? = null, + progress: Float? = null, + ) { + assertWithMessage("transitionState must be Transition") + .that(transitionState is Transition) + .isTrue() + if (currentScene != null) + assertWithMessage("currentScene does not match") + .that(transitionState.currentScene) + .isEqualTo(currentScene) + if (fromScene != null) + assertWithMessage("fromScene does not match") + .that((transitionState as? Transition)?.fromScene) + .isEqualTo(fromScene) + if (toScene != null) + assertWithMessage("toScene does not match") + .that((transitionState as? Transition)?.toScene) + .isEqualTo(toScene) + if (progress != null) + assertWithMessage("progress does not match") + .that((transitionState as? Transition)?.progress) + .isWithin(tolerance) + .of(progress) } } @@ -135,111 +191,262 @@ class SceneGestureHandlerTest { runMonotonicClockTest { TestGestureScope(coroutineScope = this).block() } } - private fun DraggableHandler.onDragStarted() = - onDragStarted(layoutSize = LAYOUT_SIZE, startedPosition = Offset.Zero) + private fun DraggableHandler.onDragStarted( + overSlop: Float = 0f, + startedPosition: Offset = Offset.Zero, + ) { + onDragStarted(startedPosition, overSlop) + // MultiPointerDraggable will always call onDelta with the initial overSlop right after + onDelta(overSlop) + } - @Test - fun testPreconditions() = runGestureTest { assertScene(currentScene = SceneA, isIdle = true) } + @Test fun testPreconditions() = runGestureTest { assertIdle(currentScene = SceneA) } @Test fun onDragStarted_shouldStartATransition() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) } @Test fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) - draggable.onDelta(pixels = deltaInPixels10) + draggable.onDelta(pixels = down(0.1f)) assertThat(progress).isEqualTo(0.1f) - draggable.onDelta(pixels = deltaInPixels10) + draggable.onDelta(pixels = down(0.1f)) assertThat(progress).isEqualTo(0.2f) } @Test fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) - draggable.onDelta(pixels = deltaInPixels10) - assertScene(currentScene = SceneA, isIdle = false) + draggable.onDelta(pixels = down(0.1f)) + assertTransition(currentScene = SceneA) draggable.onDragStopped( velocity = velocityThreshold - 0.01f, ) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) - draggable.onDelta(pixels = deltaInPixels10) - assertScene(currentScene = SceneA, isIdle = false) + draggable.onDelta(pixels = down(0.1f)) + assertTransition(currentScene = SceneA) - draggable.onDragStopped( - velocity = velocityThreshold, - ) - assertScene(currentScene = SceneC, isIdle = false) + draggable.onDragStopped(velocity = velocityThreshold) + + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } @Test - fun onDragStoppedAfterStarted_returnImmediatelyToIdle() = runGestureTest { + fun onDragStoppedAfterStarted_returnToIdle() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) draggable.onDragStopped(velocity = 0f) - assertScene(currentScene = SceneA, isIdle = true) + advanceUntilIdle() + assertIdle(currentScene = SceneA) + } + + @Test + fun onDragReversedDirection_changeToScene() = runGestureTest { + // Drag A -> B with progress 0.6 + draggable.onDragStarted() + draggable.onDelta(up(0.6f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneB, + progress = 0.6f + ) + + // Reverse direction such that A -> C now with 0.4 + draggable.onDelta(down(1f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneC, + progress = 0.4f + ) + + // After the drag stopped scene C should be committed + draggable.onDragStopped(velocity = velocityThreshold) + assertTransition(currentScene = SceneC, fromScene = SceneA, toScene = SceneC) + + // wait for the stop animation + advanceUntilIdle() + assertIdle(currentScene = SceneC) + } + + @Test + fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest { + horizontalSceneGestureHandler.draggable.onDragStarted(up(0.3f)) + assertIdle(currentScene = SceneA) + horizontalSceneGestureHandler.draggable.onDragStarted(down(0.3f)) + assertIdle(currentScene = SceneA) + } + + @Test + fun onDragIntoNoAction_startTransitionToOppositeDirection() = runGestureTest { + navigateToSceneC() + + // We are on SceneC which has no action in Down direction + draggable.onDragStarted(down(0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneB, + progress = -0.1f + ) + + // Reverse drag direction, it will consume the previous drag + draggable.onDelta(up(0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneB, + progress = 0.0f + ) + + // Continue reverse drag direction, it should record progress to Scene B + draggable.onDelta(up(0.1f)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneB, + progress = 0.1f + ) + } + + @Test + fun onDragFromEdge_startTransitionToEdgeAction() = runGestureTest { + navigateToSceneC() + + // Start dragging from the bottom + draggable.onDragStarted(up(0.1f), Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE)) + assertTransition( + currentScene = SceneC, + fromScene = SceneC, + toScene = SceneA, + progress = 0.1f + ) + } + + @Test + fun onDragToExactlyZero_toSceneIsSet() = runGestureTest { + draggable.onDragStarted(down(0.3f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneC, + progress = 0.3f + ) + draggable.onDelta(up(0.3f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneC, + progress = 0.0f + ) + } + + private fun TestGestureScope.navigateToSceneC() { + assertIdle(currentScene = SceneA) + draggable.onDragStarted(down(1f)) + draggable.onDragStopped(0f) + advanceUntilIdle() + assertIdle(currentScene = SceneC) + } + + @Test + fun onAccelaratedScroll_scrollToThirdScene() = runGestureTest { + // Drag A -> B with progress 0.2 + draggable.onDragStarted() + draggable.onDelta(up(0.2f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneB, + progress = 0.2f + ) + + // Start animation A -> B with progress 0.2 -> 1.0 + draggable.onDragStopped(velocity = -velocityThreshold) + assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB) + + // While at A -> B do a 100% screen drag (progress 1.2). This should go past B and change + // the transition to B -> C with progress 0.2 + draggable.onDragStarted() + draggable.onDelta(up(1f)) + assertTransition( + currentScene = SceneB, + fromScene = SceneB, + toScene = SceneC, + progress = 0.2f + ) + + // After the drag stopped scene C should be committed + draggable.onDragStopped(velocity = -velocityThreshold) + assertTransition(currentScene = SceneC, fromScene = SceneB, toScene = SceneC) + + // wait for the stop animation + advanceUntilIdle() + assertIdle(currentScene = SceneC) } @Test fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest { draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) - draggable.onDelta(pixels = deltaInPixels10) - assertScene(currentScene = SceneA, isIdle = false) + draggable.onDelta(pixels = down(0.1f)) + assertTransition(currentScene = SceneA) draggable.onDragStopped( velocity = velocityThreshold, ) // The stop animation is not started yet - assertThat(sceneGestureHandler.isAnimatingOffset).isFalse() + assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse() runCurrent() - assertThat(sceneGestureHandler.isAnimatingOffset).isTrue() + assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isTrue() assertThat(sceneGestureHandler.isDrivingTransition).isTrue() - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // Start a new gesture while the offset is animating draggable.onDragStarted() - assertThat(sceneGestureHandler.isAnimatingOffset).isFalse() + assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse() } @Test fun onInitialPreScroll_EdgeWithOverscroll_doNotChangeState() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.onPreScroll(available = offsetY10, source = NestedScrollSource.Drag) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun onPostScrollWithNothingAvailable_EdgeWithOverscroll_doNotChangeState() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) val consumed = nestedScroll.onPostScroll( consumed = Offset.Zero, @@ -247,13 +454,13 @@ class SceneGestureHandlerTest { source = NestedScrollSource.Drag ) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) assertThat(consumed).isEqualTo(Offset.Zero) } @Test fun onPostScrollWithSomethingAvailable_startSceneTransition() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) val consumed = nestedScroll.onPostScroll( consumed = Offset.Zero, @@ -261,7 +468,7 @@ class SceneGestureHandlerTest { source = NestedScrollSource.Drag ) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) assertThat(progress).isEqualTo(0.1f) assertThat(consumed).isEqualTo(offsetY10) } @@ -282,9 +489,9 @@ class SceneGestureHandlerTest { @Test fun afterSceneTransitionIsStarted_interceptPreScrollEvents() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.scroll(available = offsetY10) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) assertThat(progress).isEqualTo(0.1f) @@ -303,14 +510,14 @@ class SceneGestureHandlerTest { nestedScroll.scroll(available = offsetY10) assertThat(progress).isEqualTo(0.3f) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) } private suspend fun TestGestureScope.preScrollAfterSceneTransition( firstScroll: Float, secondScroll: Float ) { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) // start scene transition nestedScroll.scroll(available = Offset(0f, SCREEN_SIZE * firstScroll)) @@ -321,9 +528,6 @@ class SceneGestureHandlerTest { nestedScroll.onPreScroll(Offset(0f, SCREEN_SIZE * secondScroll), NestedScrollSource.Drag) } - // Float tolerance for comparisons - private val tolerance = 0.00001f - @Test fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest { val first = transitionInterceptionThreshold - tolerance @@ -331,7 +535,7 @@ class SceneGestureHandlerTest { preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) - assertScene(SceneA, isIdle = true) + assertIdle(SceneA) } @Test @@ -341,7 +545,7 @@ class SceneGestureHandlerTest { preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) - assertThat(progress).isWithin(tolerance).of(first + second) + assertTransition(progress = first + second) } @Test @@ -351,7 +555,7 @@ class SceneGestureHandlerTest { preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) - assertThat(progress).isWithin(tolerance).of(first + second) + assertTransition(progress = first + second) } @Test @@ -361,21 +565,21 @@ class SceneGestureHandlerTest { preScrollAfterSceneTransition(firstScroll = first, secondScroll = second) - assertScene(SceneC, isIdle = true) + assertIdle(SceneC) } @Test fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.scroll(available = offsetY10) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) nestedScroll.onPreFling(available = Velocity.Zero) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } private suspend fun TestGestureScope.flingAfterScroll( @@ -384,7 +588,7 @@ class SceneGestureHandlerTest { ) { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = use) nestedScroll.scroll(available = offsetY10) - assertScene(currentScene = SceneA, isIdle = idleAfterScroll) + if (idleAfterScroll) assertIdle(SceneA) else assertTransition(SceneA) nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) } @@ -393,40 +597,40 @@ class SceneGestureHandlerTest { fun flingAfterScroll_DuringTransitionBetweenScenes_doNothing() = runGestureTest { flingAfterScroll(use = DuringTransitionBetweenScenes, idleAfterScroll = true) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun flingAfterScroll_EdgeNoOverscroll_goToNextScene() = runGestureTest { - flingAfterScroll(use = EdgeNoOverscroll, idleAfterScroll = false) + flingAfterScroll(use = EdgeNoPreview, idleAfterScroll = false) - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } @Test fun flingAfterScroll_EdgeWithOverscroll_goToNextScene() = runGestureTest { - flingAfterScroll(use = EdgeWithOverscroll, idleAfterScroll = false) + flingAfterScroll(use = EdgeWithPreview, idleAfterScroll = false) - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } @Test fun flingAfterScroll_Always_goToNextScene() = runGestureTest { - flingAfterScroll(use = Always, idleAfterScroll = false) + flingAfterScroll(use = EdgeAlways, idleAfterScroll = false) - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } /** we started the scroll in the scene, then fling with the velocityThreshold */ @@ -440,7 +644,7 @@ class SceneGestureHandlerTest { // scroll offsetY10 is all available for parents nestedScroll.scroll(available = offsetY10) - assertScene(currentScene = SceneA, isIdle = idleAfterScroll) + if (idleAfterScroll) assertIdle(SceneA) else assertTransition(SceneA) nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) } @@ -449,64 +653,64 @@ class SceneGestureHandlerTest { fun flingAfterScrollStartedInScene_DuringTransitionBetweenScenes_doNothing() = runGestureTest { flingAfterScrollStartedInScene(use = DuringTransitionBetweenScenes, idleAfterScroll = true) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun flingAfterScrollStartedInScene_EdgeNoOverscroll_doNothing() = runGestureTest { - flingAfterScrollStartedInScene(use = EdgeNoOverscroll, idleAfterScroll = true) + flingAfterScrollStartedInScene(use = EdgeNoPreview, idleAfterScroll = true) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun flingAfterScrollStartedInScene_EdgeWithOverscroll_doOverscrollAnimation() = runGestureTest { - flingAfterScrollStartedInScene(use = EdgeWithOverscroll, idleAfterScroll = false) + flingAfterScrollStartedInScene(use = EdgeWithPreview, idleAfterScroll = false) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun flingAfterScrollStartedInScene_Always_goToNextScene() = runGestureTest { - flingAfterScrollStartedInScene(use = Always, idleAfterScroll = false) + flingAfterScrollStartedInScene(use = EdgeAlways, idleAfterScroll = false) - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } @Test fun beforeDraggableStart_drag_shouldBeIgnored() = runGestureTest { - draggable.onDelta(deltaInPixels10) - assertScene(currentScene = SceneA, isIdle = true) + draggable.onDelta(down(0.1f)) + assertIdle(currentScene = SceneA) } @Test fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest { draggable.onDragStopped(velocityThreshold) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun beforeNestedScrollStart_stop_shouldBeIgnored() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithOverscroll) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.onPreFling(Velocity(0f, velocityThreshold)) - assertScene(currentScene = SceneA, isIdle = true) + assertIdle(currentScene = SceneA) } @Test fun startNestedScrollWhileDragging() = runGestureTest { - val nestedScroll = nestedScrollConnection(nestedScrollBehavior = Always) + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) draggable.onDragStarted() - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) - draggable.onDelta(deltaInPixels10) + draggable.onDelta(down(0.1f)) assertThat(progress).isEqualTo(0.1f) // now we can intercept the scroll events @@ -515,7 +719,7 @@ class SceneGestureHandlerTest { // this should be ignored, we are scrolling now! draggable.onDragStopped(velocityThreshold) - assertScene(currentScene = SceneA, isIdle = false) + assertTransition(currentScene = SceneA) nestedScroll.scroll(available = offsetY10) assertThat(progress).isEqualTo(0.3f) @@ -524,10 +728,10 @@ class SceneGestureHandlerTest { assertThat(progress).isEqualTo(0.4f) nestedScroll.onPreFling(available = Velocity(0f, velocityThreshold)) - assertScene(currentScene = SceneC, isIdle = false) + assertTransition(currentScene = SceneC) // wait for the stop animation advanceUntilIdle() - assertScene(currentScene = SceneC, isIdle = true) + assertIdle(currentScene = SceneC) } } diff --git a/packages/SystemUI/docs/executors.md b/packages/SystemUI/docs/executors.md index 8520ce228c9d..2d9438cdcecf 100644 --- a/packages/SystemUI/docs/executors.md +++ b/packages/SystemUI/docs/executors.md @@ -14,10 +14,10 @@ Executor available, as well as our own sub-interface, [FakeExecutor][FakeExecutor] is available. [Executor]: https://developer.android.com/reference/java/util/concurrent/Executor.html -[Handler]: https://developer.android.com/reference/android/os/Handler +[Handler]: https://developer.android.com/reference/android/os/Handler.html [Runnable]: https://developer.android.com/reference/java/lang/Runnable.html [DelayableExecutor]: /packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java -[FakeExecutor]: /packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java +[FakeExecutor]: /packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutor.java ## Rationale @@ -117,7 +117,7 @@ post() | execute() | execute() postDelayed() | `none` | executeDelayed() postAtTime() | `none` | executeAtTime() -There is one notable gap in this implementation: `Handler.postAtFrontOfQueue()`. +There are some notable gaps in this implementation: `Handler.postAtFrontOfQueue()`. If you require this method, or similar, please reach out. The idea of a PriorityQueueExecutor has been floated, but will not be implemented until there is a clear need. @@ -173,13 +173,20 @@ fields in your class. If you feel that you have a use case that this does not cover, please reach out. -### Handlers Are Still Necessary +### ContentObserver + +One notable place where Handlers have been a requirement in the past is with +[ContentObserver], which takes a Handler as an argument. However, we have created +[ExecutorContentObserver], which is a hidden API that accepts an [Executor] in its +constructor instead of a [Handler], and is otherwise identical. + +[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver.html +[ExecutorContentObserver]: /core/java/android/database/ExecutorContentObserver.java -Handlers aren't going away. There are Android APIs that still require them (even -if future API development discourages them). A simple example is -[ContentObserver][ContentObserver]. Use them where necessary. +### Handlers Are Still Necessary -[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver +Handlers aren't going away. There are other Android APIs that still require them. +Avoid Handlers when possible, but use them where necessary. ## Testing (FakeExecutor) @@ -314,6 +321,15 @@ clock.setUptimeMillis(500); The Runnables _will not_ interleave. All of one Executor's callbacks will run, then all of the other's. +### Testing Handlers without Loopers + +If a [Handler] is required because it is used by Android APIs, but is only +used in simple ways (i.e. just `Handler.post(Runnable)`), you may still +want the benefits of [FakeExecutor] when writing your tests, which +you can get by wrapping the [Executor] in a mock for testing. This can be +done with `com.android.systemui.util.concurrency.mockExecutorHandler` in +`MockExecutorHandler.kt`. + ### TestableLooper.RunWithLooper As long as you're using FakeExecutors in all the code under test (and no diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 543b2910bbda..695d888d94f5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -465,29 +465,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { } @Test - fun showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() { - // GIVEN the current security method is SimPin - whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) - whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) - .thenReturn(false) - underTest.showSecurityScreen(SecurityMode.SimPin) - - // WHEN a request is made from the SimPin screens to show the next security method - whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN) - underTest.showNextSecurityScreenOrFinish( - /* authenticated= */ true, - TARGET_USER_ID, - /* bypassSecondaryLockScreen= */ true, - SecurityMode.SimPin - ) - - // THEN the next security method of PIN is set, and the keyguard is not marked as done - verify(viewMediatorCallback, never()).keyguardDonePending(anyInt()) - verify(viewMediatorCallback, never()).keyguardDone(anyInt()) - Truth.assertThat(underTest.currentSecurityMode).isEqualTo(SecurityMode.PIN) - } - - @Test fun showNextSecurityScreenOrFinish_DeviceNotSecure() { // GIVEN the current security method is SimPin whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) @@ -578,6 +555,57 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { } @Test + fun showNextSecurityScreenOrFinish_SimPin_Password() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)) + .thenReturn(SecurityMode.Password) + // WHEN security method is SWIPE + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(false) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN we will not show the password screen. + verify(viewFlipperController, never()) + .getSecurityView(eq(SecurityMode.Password), any(), any()) + } + + @Test + fun showNextSecurityScreenOrFinish_SimPin_SimPin() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)) + .thenReturn(SecurityMode.SimPin) + // WHEN security method is SWIPE + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + whenever(deviceProvisionedController.isUserSetup(anyInt())).thenReturn(false) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN we will not show the password screen. + verify(viewFlipperController).getSecurityView(eq(SecurityMode.SimPin), any(), any()) + } + + @Test fun onSwipeUp_forwardsItToFaceAuthInteractor() { val registeredSwipeListener = registeredSwipeListener setupGetSecurityView(SecurityMode.Password) diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index 94c3bde29597..84d735430edd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -34,11 +34,14 @@ import com.android.systemui.util.mockito.any import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -63,6 +66,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardMessageAreaController: KeyguardMessageAreaController<BouncerKeyguardMessageArea> + private val updateMonitorCallbackArgumentCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) @Before fun setup() { @@ -95,6 +100,9 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { mSelectedUserInteractor ) underTest.init() + underTest.onResume(0) + verify(keyguardUpdateMonitor) + .registerCallback(updateMonitorCallbackArgumentCaptor.capture()) } @Test @@ -111,6 +119,7 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { @Test fun onResume() { + reset(keyguardUpdateMonitor) underTest.onResume(KeyguardSecurityView.VIEW_REVEALED) verify(keyguardUpdateMonitor) .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) @@ -137,4 +146,22 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { underTest.resetState() verify(keyguardMessageAreaController).setMessage("") } + + @Test + fun onSimStateChangedFromPinToPuk_showsCurrentSecurityScreen() { + updateMonitorCallbackArgumentCaptor.value.onSimStateChanged( + /* subId= */ 0, + /* slotId= */ 0, + TelephonyManager.SIM_STATE_PIN_REQUIRED + ) + verify(keyguardSecurityCallback, never()).showCurrentSecurityScreen() + + updateMonitorCallbackArgumentCaptor.value.onSimStateChanged( + /* subId= */ 0, + /* slotId= */ 0, + TelephonyManager.SIM_STATE_PUK_REQUIRED + ) + + verify(keyguardSecurityCallback).showCurrentSecurityScreen() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index d968c1bb54bb..08cd7edba6af 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -21,14 +21,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat -import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent @@ -78,12 +76,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun authenticate_withCorrectPin_succeeds() = testScope.runTest { - val throttling by collectLastValue(underTest.throttling) + val lockout by collectLastValue(underTest.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(throttling).isNull() + assertThat(lockout).isNull() + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0) } @Test @@ -131,14 +130,15 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun authenticate_withCorrectPassword_succeeds() = testScope.runTest { - val throttling by collectLastValue(underTest.throttling) + val lockout by collectLastValue(underTest.lockout) utils.authenticationRepository.setAuthenticationMethod( AuthenticationMethodModel.Password ) assertThat(underTest.authenticate("password".toList())) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(throttling).isNull() + assertThat(lockout).isNull() + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0) } @Test @@ -187,7 +187,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) - val throttling by collectLastValue(underTest.throttling) + val lockout by collectLastValue(underTest.lockout) utils.authenticationRepository.apply { setAuthenticationMethod(AuthenticationMethodModel.Pin) setAutoConfirmFeatureEnabled(true) @@ -203,7 +203,8 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isEqualTo(AuthenticationResult.SKIPPED) - assertThat(throttling).isNull() + assertThat(lockout).isNull() + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0) } @Test @@ -264,7 +265,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNull() = + fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringLockout_returnsNull() = testScope.runTest { val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked) @@ -272,7 +273,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.apply { setAuthenticationMethod(AuthenticationMethodModel.Pin) setAutoConfirmFeatureEnabled(true) - setThrottleDuration(42) + setLockoutDuration(42) } val authResult = @@ -315,69 +316,121 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test - fun throttling() = + fun isAutoConfirmEnabled_featureDisabled_returnsFalse() = testScope.runTest { - val throttling by collectLastValue(underTest.throttling) + val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) + utils.authenticationRepository.setAutoConfirmFeatureEnabled(false) + + assertThat(isAutoConfirmEnabled).isFalse() + } + + @Test + fun isAutoConfirmEnabled_featureEnabled_returnsTrue() = + testScope.runTest { + val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) + utils.authenticationRepository.setAutoConfirmFeatureEnabled(true) + + assertThat(isAutoConfirmEnabled).isTrue() + } + + @Test + fun isAutoConfirmEnabled_featureEnabledButDisabledByLockout() = + testScope.runTest { + val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled) + val lockout by collectLastValue(underTest.lockout) + utils.authenticationRepository.setAutoConfirmFeatureEnabled(true) + + // The feature is enabled. + assertThat(isAutoConfirmEnabled).isTrue() + + // Make many wrong attempts to trigger lockout. + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { + underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN + } + assertThat(lockout).isNotNull() + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1) + + // Lockout disabled auto-confirm. + assertThat(isAutoConfirmEnabled).isFalse() + + // Move the clock forward one more second, to completely finish the lockout period: + advanceTimeBy(FakeAuthenticationRepository.LOCKOUT_DURATION_MS + 1000L) + assertThat(lockout).isNull() + + // Auto-confirm is still disabled, because lockout occurred at least once in this + // session. + assertThat(isAutoConfirmEnabled).isFalse() + + // Correct PIN and unlocks successfully, resetting the 'session'. + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) + .isEqualTo(AuthenticationResult.SUCCEEDED) + + // Auto-confirm is re-enabled. + assertThat(isAutoConfirmEnabled).isTrue() + + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1) + } + + @Test + fun lockout() = + testScope.runTest { + val lockout by collectLastValue(underTest.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) - assertThat(throttling).isNull() + assertThat(lockout).isNull() - // Make many wrong attempts, but just shy of what's needed to get throttled: - repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) { + // Make many wrong attempts, but just shy of what's needed to get locked out: + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN - assertThat(throttling).isNull() + assertThat(lockout).isNull() } - // Make one more wrong attempt, leading to throttling: + // Make one more wrong attempt, leading to lockout: underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN - assertThat(throttling) + assertThat(lockout) .isEqualTo( - AuthenticationThrottlingModel( + AuthenticationLockoutModel( failedAttemptCount = - FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, - remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, + remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS, ) ) + assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1) - // Correct PIN, but throttled, so doesn't attempt it: + // Correct PIN, but locked out, so doesn't attempt it: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) - assertThat(throttling) + assertThat(lockout) .isEqualTo( - AuthenticationThrottlingModel( + AuthenticationLockoutModel( failedAttemptCount = - FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, - remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, + remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS, ) ) - // Move the clock forward to ALMOST skip the throttling, leaving one second to go: - val throttleTimeoutSec = - FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds - .toInt() - repeat(throttleTimeoutSec - 1) { time -> + // Move the clock forward to ALMOST skip the lockout, leaving one second to go: + val lockoutTimeoutSec = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS + repeat(FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS - 1) { time -> advanceTimeBy(1000) - assertThat(throttling) + assertThat(lockout) .isEqualTo( - AuthenticationThrottlingModel( + AuthenticationLockoutModel( failedAttemptCount = - FakeAuthenticationRepository - .MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, - remainingMs = - ((throttleTimeoutSec - (time + 1)).seconds.inWholeMilliseconds) - .toInt(), + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, + remainingSeconds = lockoutTimeoutSec - (time + 1), ) ) } - // Move the clock forward one more second, to completely finish the throttling period: + // Move the clock forward one more second, to completely finish the lockout period: advanceTimeBy(1000) - assertThat(throttling).isNull() + assertThat(lockout).isNull() - // Correct PIN and no longer throttled so unlocks successfully: + // Correct PIN and no longer locked out so unlocks successfully: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(throttling).isNull() + assertThat(lockout).isNull() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index cbb772f49c93..0ab596c82d6f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -21,6 +21,8 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel +import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF +import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl @@ -56,10 +58,12 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -539,4 +543,77 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) job.cancel() } + + @Test + fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() = + testScope.runTest { + // GIVEN view is attached + mController.onViewAttached() + Mockito.reset(mView) + + val job = mController.listenForLockscreenAodTransitions(this) + // WHEN lockscreen to aod transition is cancelled + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.AOD, + value = 1f, + transitionState = TransitionState.CANCELED + ) + ) + runCurrent() + + // THEN doze amount is NOT updated to zero + verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt()) + job.cancel() + } + + @Test + fun dreamingToAod_dozeAmountChanged() = + testScope.runTest { + // GIVEN view is attached + mController.onViewAttached() + Mockito.reset(mView) + + val job = mController.listenForDreamingToAodTransitions(this) + // WHEN dreaming to aod transition in progress + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.DREAMING, + to = KeyguardState.AOD, + value = .3f, + transitionState = TransitionState.RUNNING + ) + ) + runCurrent() + + // THEN doze amount is updated to + verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) + job.cancel() + } + + @Test + fun alternateBouncerToAod_dozeAmountChanged() = + testScope.runTest { + // GIVEN view is attached + mController.onViewAttached() + Mockito.reset(mView) + + val job = mController.listenForAlternateBouncerToAodTransitions(this) + // WHEN alternate bouncer to aod transition in progress + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.AOD, + value = .3f, + transitionState = TransitionState.RUNNING + ) + ) + runCurrent() + + // THEN doze amount is updated to + verify(mView) + .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) + job.cancel() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 04f6cd30fc32..9b1df7c0ffc0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -21,15 +21,14 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.AuthenticationResult +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor import com.android.systemui.res.R import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat -import kotlin.math.ceil import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceTimeBy @@ -247,47 +246,42 @@ class BouncerInteractorTest : SysuiTestCase() { } @Test - fun throttling() = + fun lockout() = testScope.runTest { - val throttling by collectLastValue(underTest.throttling) + val lockout by collectLastValue(underTest.lockout) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(throttling).isNull() - repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times -> + assertThat(lockout).isNull() + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times -> // Wrong PIN. assertThat(underTest.authenticate(listOf(6, 7, 8, 9))) .isEqualTo(AuthenticationResult.FAILED) - if ( - times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1 - ) { + if (times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) } } - assertThat(throttling) + assertThat(lockout) .isEqualTo( - AuthenticationThrottlingModel( + AuthenticationLockoutModel( failedAttemptCount = - FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, - remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT, + remainingSeconds = FakeAuthenticationRepository.LOCKOUT_DURATION_SECONDS, ) ) assertTryAgainMessage( message, - FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds - .toInt() + FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) - // Correct PIN, but throttled, so doesn't change away from the bouncer scene: + // Correct PIN, but locked out, so doesn't change away from the bouncer scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SKIPPED) assertTryAgainMessage( message, - FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds - .toInt() + FakeAuthenticationRepository.LOCKOUT_DURATION_MS.milliseconds.inWholeSeconds.toInt() ) - throttling?.remainingMs?.let { remainingMs -> - val seconds = ceil(remainingMs / 1000f).toInt() + lockout?.remainingSeconds?.let { seconds -> repeat(seconds) { time -> advanceTimeBy(1000) val remainingTimeSec = seconds - time - 1 @@ -297,12 +291,12 @@ class BouncerInteractorTest : SysuiTestCase() { } } assertThat(message).isEqualTo("") - assertThat(throttling).isNull() + assertThat(lockout).isNull() - // Correct PIN and no longer throttled so changes to the Gone scene: + // Correct PIN and no longer locked out so changes to the Gone scene: assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)) .isEqualTo(AuthenticationResult.SUCCEEDED) - assertThat(throttling).isNull() + assertThat(lockout).isNull() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 45c186dc3a77..2f0843b202a0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -35,7 +35,6 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { private val utils = SceneTestUtils(this) private val testScope = utils.testScope - private val sceneInteractor = utils.sceneInteractor() private val bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = utils.authenticationInteractor(), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 75d6a007b4aa..16a935943dbf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.flags.Flags import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -134,17 +135,19 @@ class BouncerViewModelTest : SysuiTestCase() { fun message() = testScope.runTest { val message by collectLastValue(underTest.message) - val throttling by collectLastValue(bouncerInteractor.throttling) + val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(message?.isUpdateAnimated).isTrue() - repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) } assertThat(message?.isUpdateAnimated).isFalse() - throttling?.remainingMs?.let { remainingMs -> advanceTimeBy(remainingMs.toLong()) } + lockout?.remainingSeconds?.let { remainingSeconds -> + advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) + } assertThat(message?.isUpdateAnimated).isTrue() } @@ -157,35 +160,37 @@ class BouncerViewModelTest : SysuiTestCase() { authViewModel?.isInputEnabled ?: emptyFlow() } ) - val throttling by collectLastValue(bouncerInteractor.throttling) + val lockout by collectLastValue(bouncerInteractor.lockout) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(isInputEnabled).isTrue() - repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) } assertThat(isInputEnabled).isFalse() - throttling?.remainingMs?.let { milliseconds -> advanceTimeBy(milliseconds.toLong()) } + lockout?.remainingSeconds?.let { remainingSeconds -> + advanceTimeBy(remainingSeconds.seconds.inWholeMilliseconds) + } assertThat(isInputEnabled).isTrue() } @Test - fun throttlingDialogMessage() = + fun dialogMessage() = testScope.runTest { - val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage) + val dialogMessage by collectLastValue(underTest.dialogMessage) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { // Wrong PIN. - assertThat(throttlingDialogMessage).isNull() + assertThat(dialogMessage).isNull() bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) } - assertThat(throttlingDialogMessage).isNotEmpty() + assertThat(dialogMessage).isNotEmpty() - underTest.onThrottlingDialogDismissed() - assertThat(throttlingDialogMessage).isNull() + underTest.onDialogDismissed() + assertThat(dialogMessage).isNull() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 64f294600be2..6d6baa57bb9d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.res.R @@ -243,12 +243,12 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { } @Test - fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() = + fun onImeVisibilityChanged_falseAfterTrue_whileLockedOut_doesNothing() = testScope.runTest { val events by collectValues(bouncerInteractor.onImeHiddenByUser) assertThat(events).isEmpty() underTest.onImeVisibilityChanged(isVisible = true) - setThrottling(true) + setLockout(true) underTest.onImeVisibilityChanged(isVisible = false) @@ -284,11 +284,11 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { } @Test - fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() = + fun isTextFieldFocusRequested_focusLostWhileLockedOut_staysFalse() = testScope.runTest { val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested) underTest.onTextFieldFocusChanged(isFocused = true) - setThrottling(true) + setLockout(true) underTest.onTextFieldFocusChanged(isFocused = false) @@ -296,14 +296,14 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { } @Test - fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() = + fun isTextFieldFocusRequested_lockoutCountdownEnds_becomesTrue() = testScope.runTest { val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested) underTest.onTextFieldFocusChanged(isFocused = true) - setThrottling(true) + setLockout(true) underTest.onTextFieldFocusChanged(isFocused = false) - setThrottling(false) + setLockout(false) assertThat(isTextFieldFocusRequested).isTrue() } @@ -327,24 +327,24 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { switchToScene(SceneKey.Bouncer) } - private suspend fun TestScope.setThrottling( - isThrottling: Boolean, + private suspend fun TestScope.setLockout( + isLockedOut: Boolean, failedAttemptCount: Int = 5, ) { - if (isThrottling) { + if (isLockedOut) { repeat(failedAttemptCount) { authenticationRepository.reportAuthenticationAttempt(false) } - val remainingTimeMs = 30_000 - authenticationRepository.setThrottleDuration(remainingTimeMs) - authenticationRepository.throttling.value = - AuthenticationThrottlingModel( + val remainingTimeSeconds = 30 + authenticationRepository.setLockoutDuration(remainingTimeSeconds * 1000) + authenticationRepository.lockout.value = + AuthenticationLockoutModel( failedAttemptCount = failedAttemptCount, - remainingMs = remainingTimeMs, + remainingSeconds = remainingTimeSeconds, ) } else { authenticationRepository.reportAuthenticationAttempt(true) - authenticationRepository.throttling.value = null + authenticationRepository.lockout.value = null } runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 862c39c9d4cc..8971423edd52 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -304,13 +304,12 @@ class PatternBouncerViewModelTest : SysuiTestCase() { fun onDragEnd_whenPatternTooShort() = testScope.runTest { val message by collectLastValue(bouncerViewModel.message) - val throttlingDialogMessage by - collectLastValue(bouncerViewModel.throttlingDialogMessage) + val dialogMessage by collectLastValue(bouncerViewModel.dialogMessage) lockDeviceAndOpenPatternBouncer() // Enter a pattern that's too short more than enough times that would normally trigger - // throttling if the pattern were not too short and wrong: - val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING + 1 + // lockout if the pattern were not too short and wrong: + val attempts = FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT + 1 repeat(attempts) { attempt -> underTest.onDragStart() CORRECT_PATTERN.subList( @@ -328,7 +327,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() { underTest.onDragEnd() assertWithMessage("Attempt #$attempt").that(message?.text).isEqualTo(WRONG_PATTERN) - assertWithMessage("Attempt #$attempt").that(throttlingDialogMessage).isNull() + assertWithMessage("Attempt #$attempt").that(dialogMessage).isNull() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt index 97ac8c62d69d..d3049d9080f3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt @@ -6,6 +6,7 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.scene.SceneTestUtils import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -38,6 +39,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { private val testUtils = SceneTestUtils(this) private val testScope = testUtils.testScope private val userRepository = FakeUserRepository() + private val keyguardRepository = FakeKeyguardRepository() private lateinit var underTest: DeviceEntryRepository @@ -55,6 +57,7 @@ class DeviceEntryRepositoryTest : SysuiTestCase() { lockPatternUtils = lockPatternUtils, keyguardBypassController = keyguardBypassController, keyguardStateController = keyguardStateController, + keyguardRepository = keyguardRepository, ) testScope.runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index bc4bae0ed959..34f703bc0ca7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -49,8 +49,10 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -93,6 +95,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { private lateinit var dockManager: DockManagerFake private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository + private val kosmos = testKosmos() + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -179,6 +183,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = withDeps.keyguardInteractor, + shadeInteractor = kosmos.shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -339,6 +344,31 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() = + testScope.runTest { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + repository.setKeyguardShowing(false) + repository.setIsDozing(true) + homeControls.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + activationState = ActivationState.Active, + ) + ) + + val collectedValue by + collectLastValue( + underTest.quickAffordanceAlwaysVisible( + KeyguardQuickAffordancePosition.BOTTOM_START + ) + ) + + assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java) + } + + @Test fun quickAffordanceAlwaysVisible_evenWhenLockScreenNotShowingAndDozing() = testScope.runTest { repository.setKeyguardShowing(false) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt index 9226c0d61a3c..a346e8b45795 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt @@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -48,7 +48,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().apply { - featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } } private val testScope = kosmos.testScope private val repository = kosmos.fakeKeyguardTransitionRepository diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt index bcad72bef1e6..274bde1ccfdf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.common.ui.data.repository.fakeConfigurationRepositor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -49,7 +49,7 @@ import org.junit.runner.RunWith class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().apply { - featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } } private val testScope = kosmos.testScope private val repository = kosmos.fakeKeyguardTransitionRepository diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 78d87a680c5b..f027bc849e51 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState @@ -43,7 +43,7 @@ import org.junit.runner.RunWith class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { val kosmos = testKosmos().apply { - featureFlagsClassic.apply { + fakeFeatureFlagsClassic.apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) set(Flags.FULL_SCREEN_USER_SWITCHER, false) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 8c896a6a1709..1e2784a622b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -25,11 +25,10 @@ import android.service.quicksettings.Tile import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE +import com.android.systemui.Flags.FLAG_QS_NEW_TILES import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.nano.SystemUIProtoDump -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTile.BooleanState import com.android.systemui.qs.FakeQSFactory @@ -81,8 +80,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val tileFactory = FakeQSFactory(::tileCreator) private val customTileAddedRepository: CustomTileAddedRepository = FakeCustomTileAddedRepository() - private val featureFlags = FakeFeatureFlags() - private val pipelineFlags = QSPipelineFlagsRepository(featureFlags) + private val pipelineFlags = QSPipelineFlagsRepository() private val tileLifecycleManagerFactory = TLMFactory() @Mock private lateinit var customTileStatePersister: CustomTileStatePersister @@ -100,14 +98,12 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private lateinit var underTest: CurrentTilesInteractorImpl - @OptIn(ExperimentalCoroutinesApi::class) @Before fun setup() { MockitoAnnotations.initMocks(this) mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE) - // TODO(b/299909337): Add test checking the new factory is used when the flag is on - featureFlags.set(Flags.QS_PIPELINE_NEW_TILES, true) + mSetFlagsRule.enableFlags(FLAG_QS_NEW_TILES) userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt index 00572d3163eb..7f7490d9ecc3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor + import android.os.UserHandle import android.testing.LeakCheck import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -22,7 +24,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger -import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel import com.android.systemui.utils.leaks.FakeFlashlightController import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt index f819f53838b8..28d43b369dd4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt @@ -14,12 +14,13 @@ * limitations under the License. */ +package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor + import android.app.ActivityManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click -import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel import com.android.systemui.statusbar.policy.FlashlightController import com.android.systemui.util.mockito.mock diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt new file mode 100644 index 000000000000..4b9625107745 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.saver.domain + +import android.content.SharedPreferences +import android.testing.LeakCheck +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.utils.leaks.FakeDataSaverController +import kotlin.coroutines.EmptyCoroutineContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify + +/** Test [DataSaverDialogDelegate]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class DataSaverDialogDelegateTest : SysuiTestCase() { + + private val dataSaverController = FakeDataSaverController(LeakCheck()) + + private lateinit var sysuiDialogFactory: SystemUIDialog.Factory + private lateinit var sysuiDialog: SystemUIDialog + private lateinit var dataSaverDialogDelegate: DataSaverDialogDelegate + + @Before + fun setup() { + sysuiDialog = mock<SystemUIDialog>() + sysuiDialogFactory = mock<SystemUIDialog.Factory>() + + dataSaverDialogDelegate = + DataSaverDialogDelegate( + sysuiDialogFactory, + context, + EmptyCoroutineContext, + dataSaverController, + mock<SharedPreferences>() + ) + + whenever(sysuiDialogFactory.create(eq(dataSaverDialogDelegate), eq(context))) + .thenReturn(sysuiDialog) + } + @Test + fun delegateSetsDialogTitleCorrectly() { + val expectedResId = R.string.data_saver_enable_title + + dataSaverDialogDelegate.onCreate(sysuiDialog, null) + + verify(sysuiDialog).setTitle(eq(expectedResId)) + } + + @Test + fun delegateSetsDialogMessageCorrectly() { + val expectedResId = R.string.data_saver_description + + dataSaverDialogDelegate.onCreate(sysuiDialog, null) + + verify(sysuiDialog).setMessage(expectedResId) + } + + @Test + fun delegateSetsDialogPositiveButtonCorrectly() { + val expectedResId = R.string.data_saver_enable_button + + dataSaverDialogDelegate.onCreate(sysuiDialog, null) + + verify(sysuiDialog).setPositiveButton(eq(expectedResId), any()) + } + + @Test + fun delegateSetsDialogCancelButtonCorrectly() { + val expectedResId = R.string.cancel + + dataSaverDialogDelegate.onCreate(sysuiDialog, null) + + verify(sysuiDialog).setNeutralButton(eq(expectedResId), eq(null)) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt new file mode 100644 index 000000000000..d1824129590b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt @@ -0,0 +1,95 @@ +/* + * 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.tiles.impl.saver.domain + +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DataSaverTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig + + // Using lazy (versus =) to make sure we override the right context -- see b/311612168 + private val mapper by lazy { DataSaverTileMapper(context.orCreateTestableResources.resources) } + + @Test + fun activeStateMatchesEnabledModel() { + val inputModel = DataSaverTileModel(true) + + val outputState = mapper.map(dataSaverTileConfig, inputModel) + + val expectedState = + createDataSaverTileState( + QSTileState.ActivationState.ACTIVE, + R.drawable.qs_data_saver_icon_on + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun inactiveStateMatchesDisabledModel() { + val inputModel = DataSaverTileModel(false) + + val outputState = mapper.map(dataSaverTileConfig, inputModel) + + val expectedState = + createDataSaverTileState( + QSTileState.ActivationState.INACTIVE, + R.drawable.qs_data_saver_icon_off + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createDataSaverTileState( + activationState: QSTileState.ActivationState, + iconRes: Int + ): QSTileState { + val label = context.getString(R.string.data_saver) + val secondaryLabel = + if (activationState == QSTileState.ActivationState.ACTIVE) + context.resources.getStringArray(R.array.tile_states_saver)[2] + else if (activationState == QSTileState.ActivationState.INACTIVE) + context.resources.getStringArray(R.array.tile_states_saver)[1] + else context.resources.getStringArray(R.array.tile_states_saver)[0] + + return QSTileState( + { Icon.Resource(iconRes, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt new file mode 100644 index 000000000000..819bd03437f4 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt @@ -0,0 +1,75 @@ +/* + * 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.tiles.impl.saver.domain.interactor + +import android.os.UserHandle +import android.testing.LeakCheck +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.utils.leaks.FakeDataSaverController +import com.google.common.truth.Truth +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class DataSaverTileDataInteractorTest : SysuiTestCase() { + private val controller: FakeDataSaverController = FakeDataSaverController(LeakCheck()) + private val underTest: DataSaverTileDataInteractor = DataSaverTileDataInteractor(controller) + + @Test + fun isAvailableRegardlessOfController() = runTest { + controller.setDataSaverEnabled(false) + + runCurrent() + val availability by collectLastValue(underTest.availability(TEST_USER)) + + Truth.assertThat(availability).isTrue() + } + + @Test + fun dataMatchesController() = runTest { + controller.setDataSaverEnabled(false) + val flowValues: List<DataSaverTileModel> by + collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) + + runCurrent() + controller.setDataSaverEnabled(true) + runCurrent() + controller.setDataSaverEnabled(false) + runCurrent() + + Truth.assertThat(flowValues.size).isEqualTo(3) + Truth.assertThat(flowValues.map { it.isEnabled }) + .containsExactly(false, true, false) + .inOrder() + } + + private companion object { + val TEST_USER = UserHandle.of(1)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..7091cb3b259c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt @@ -0,0 +1,183 @@ +/* + * 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.tiles.impl.saver.domain.interactor + +import android.content.Context +import android.content.SharedPreferences +import android.provider.Settings +import android.testing.LeakCheck +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.intentInputs +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.settings.UserFileManager +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.utils.leaks.FakeDataSaverController +import com.google.common.truth.Truth.assertThat +import kotlin.coroutines.EmptyCoroutineContext +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DataSaverTileUserActionInteractorTest : SysuiTestCase() { + private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler() + private val dataSaverController = FakeDataSaverController(LeakCheck()) + + private lateinit var userFileManager: UserFileManager + private lateinit var sharedPreferences: SharedPreferences + private lateinit var dialogFactory: SystemUIDialog.Factory + private lateinit var underTest: DataSaverTileUserActionInteractor + + @Before + fun setup() { + userFileManager = mock<UserFileManager>() + sharedPreferences = mock<SharedPreferences>() + dialogFactory = mock<SystemUIDialog.Factory>() + whenever( + userFileManager.getSharedPreferences( + eq(DataSaverTileUserActionInteractor.PREFS), + eq(Context.MODE_PRIVATE), + eq(context.userId) + ) + ) + .thenReturn(sharedPreferences) + + underTest = + DataSaverTileUserActionInteractor( + context, + EmptyCoroutineContext, + EmptyCoroutineContext, + dataSaverController, + qsTileIntentUserActionHandler, + mock<DialogLaunchAnimator>(), + dialogFactory, + userFileManager, + ) + } + + /** Since the dialog was shown before, we expect the click to enable the controller. */ + @Test + fun handleClickToEnableDialogShownBefore() = runTest { + whenever( + sharedPreferences.getBoolean( + eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN), + any() + ) + ) + .thenReturn(true) + val stateBeforeClick = false + + underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(stateBeforeClick))) + + assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!stateBeforeClick) + } + + /** + * The first time the tile is clicked to turn on we expect (1) the enabled state to not change + * and (2) the dialog to be shown instead. + */ + @Test + fun handleClickToEnableDialogNotShownBefore() = runTest { + whenever( + sharedPreferences.getBoolean( + eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN), + any() + ) + ) + .thenReturn(false) + val mockDialog = mock<SystemUIDialog>() + whenever(dialogFactory.create(any(), any())).thenReturn(mockDialog) + val stateBeforeClick = false + + val input = QSTileInputTestKtx.click(DataSaverTileModel(stateBeforeClick)) + underTest.handleInput(input) + + assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(stateBeforeClick) + verify(mockDialog).show() + } + + /** Disabling should flip the state, even if the dialog was not shown before. */ + @Test + fun handleClickToDisableDialogNotShownBefore() = runTest { + whenever( + sharedPreferences.getBoolean( + eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN), + any() + ) + ) + .thenReturn(false) + val enabledBeforeClick = true + + underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(enabledBeforeClick))) + + assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!enabledBeforeClick) + } + + @Test + fun handleClickToDisableDialogShownBefore() = runTest { + whenever( + sharedPreferences.getBoolean( + eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN), + any() + ) + ) + .thenReturn(true) + val enabledBeforeClick = true + + underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(enabledBeforeClick))) + + assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!enabledBeforeClick) + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabledState = true + + underTest.handleInput(QSTileInputTestKtx.longClick(DataSaverTileModel(enabledState))) + + assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1) + val intentInput = qsTileIntentUserActionHandler.intentInputs.last() + val actualIntentAction = intentInput.intent.action + val expectedIntentAction = Settings.ACTION_DATA_SAVER_SETTINGS + assertThat(actualIntentAction).isEqualTo(expectedIntentAction) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabledState = false + + underTest.handleInput(QSTileInputTestKtx.longClick(DataSaverTileModel(enabledState))) + + assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1) + val intentInput = qsTileIntentUserActionHandler.intentInputs.last() + val actualIntentAction = intentInput.intent.action + val expectedIntentAction = Settings.ACTION_DATA_SAVER_SETTINGS + assertThat(actualIntentAction).isEqualTo(expectedIntentAction) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 70be03146e17..c110de969b57 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -63,7 +63,6 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage -import kotlin.test.Ignore import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -396,7 +395,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { } @Test - @Ignore("b/315130482") fun deviceGoesToSleep_wakeUp_unlock() = testScope.runTest { unlockDevice() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 3cb97e369a67..61d55f0ae667 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.scene.domain.startable import android.os.PowerManager +import android.platform.test.annotations.EnableFlags import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations @@ -55,6 +55,7 @@ import org.mockito.Mockito.verify @SmallTest @RunWith(AndroidJUnit4::class) +@EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER) class SceneContainerStartableTest : SysuiTestCase() { private val utils = SceneTestUtils(this) @@ -93,11 +94,6 @@ class SceneContainerStartableTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, ) - @Before - fun setUp() { - mSetFlagsRule.enableFlags(AconfigFlags.FLAG_SCENE_CONTAINER) - } - @Test fun hydrateVisibility() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index f04dfd1b8fdb..4cdb08afb22e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags @@ -51,7 +51,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { private val kosmos = testKosmos().apply { sceneContainerFlags = FakeSceneContainerFlags(enabled = true) - featureFlagsClassic.apply { + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.NSSL_DEBUG_LINES, false) } @@ -67,9 +67,24 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { val bounds by collectLastValue(appearanceViewModel.stackBounds) val top = 200f + val left = 0f val bottom = 550f - placeholderViewModel.onBoundsChanged(top, bottom) - assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom)) + val right = 100f + placeholderViewModel.onBoundsChanged( + left = left, + top = top, + right = right, + bottom = bottom + ) + assertThat(bounds) + .isEqualTo( + NotificationContainerBounds( + left = left, + top = top, + right = right, + bottom = bottom + ) + ) } @Test diff --git a/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml b/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml new file mode 100644 index 000000000000..bd604317bbb8 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_record_issue_icon_off.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M5.76 12.89c0 3.28 2.54 5.97 5.76 6.22v-8.26h-5.4c-.23.64-.36 1.33-.36 2.04zm9.27-5.45l1.01-1.59c.19-.29.1-.67-.19-.86-.29-.19-.68-.1-.86.19l-1.12 1.76c-.59-.19-1.22-.29-1.87-.29s-1.28.1-1.87.29L9.01 5.18c-.18-.29-.57-.38-.86-.19-.29.18-.38.57-.19.86l1.01 1.59c-1.02.57-1.86 1.43-2.43 2.45h10.92c-.57-1.02-1.41-1.88-2.43-2.45zm2.85 3.41h-5.4v8.26c3.22-.25 5.76-2.93 5.76-6.22 0-.71-.13-1.4-.36-2.04z" + android:fillColor="#000000"/> +</vector> diff --git a/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml b/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml new file mode 100644 index 000000000000..fadaf7896ae4 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_record_issue_icon_on.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M5.76 12.89c0 3.28 2.54 5.97 5.76 6.22v-8.26h-5.4c-.23.64-.36 1.33-.36 2.04zm9.27-5.45l1.01-1.59c.19-.29.1-.67-.19-.86-.29-.19-.68-.1-.86.19l-1.12 1.76c-.59-.19-1.22-.29-1.87-.29s-1.28.1-1.87.29L9.01 5.18c-.18-.29-.57-.38-.86-.19-.29.18-.38.57-.19.86l1.01 1.59c-1.02.57-1.86 1.43-2.43 2.45h10.92c-.57-1.02-1.41-1.88-2.43-2.45zm2.85 3.41h-5.4v8.26c3.22-.25 5.76-2.93 5.76-6.22 0-.71-.13-1.4-.36-2.04z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 66c57fc2a9ac..6d7ce0623817 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -106,5 +106,5 @@ </FrameLayout> <include layout="@layout/ambient_indication" - android:id="@+id/ambient_indication_container" /> + android:id="@id/ambient_indication_container" /> </com.android.systemui.statusbar.phone.KeyguardBottomAreaView> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 09f37669cb74..14f6821bed04 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knoppie <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppyl"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Afpyl"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Linkspyl"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Regspyl"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Middel"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spasie"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Kennisgewings"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Kortpadsleutels"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Wissel sleutelborduitleg"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"of"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Maak soeknavraag skoon"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kortpaaie"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Soek kortpaaie"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Geen kortpaaie gevind nie"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Invoer"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Maak apps oop"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Huidige app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Wys tans soekresultate"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Wys tans stelselkortpaaie"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Wys tans invoerkortpaaie"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Wys tans kortpaaie wat apps oopmaak"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Wys tans kortpaaie vir die huidige app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Bekyk kennisgewings"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Neem skermskoot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Wys kortpaaie"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 1c065bb20bec..17ff32fad2c8 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"الزر <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"سهم متّجه للأعلى"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"سهم متّجه للأسفل"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"سهم متّجه لليسار"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"سهم متّجه لليمين"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"وسط"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"مسافة"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"الإشعارات"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"اختصارات لوحة المفاتيح"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تبديل تنسيق لوحة المفاتيح"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"أو"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"محو طلب البحث"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"الاختصارات"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"البحث في الاختصارات"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"لم يُعثَر على اختصارات."</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"إدخال"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"فتح التطبيقات"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"التطبيق الحالي"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"جارٍ عرض نتائج البحث"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"جارٍ عرض اختصارات النظام"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"جارٍ عرض اختصارات الإدخال"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"جارٍ عرض اختصارات فتح التطبيقات"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"جارٍ عرض اختصارات التطبيق الحالي"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"عرض الإشعارات"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"أخذ لقطة شاشة"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"عرض الاختصارات"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 0c5f1044b1d5..457f931d94fb 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বুটাম"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"হ\'ম"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"উভতি যাওক"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ওপৰলৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"তললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাওঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"সোঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"স্ক্ৰীনৰ মাজত"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"স্পেচ"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"জাননীসমূহ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"কীব\'ৰ্ড শ্বৰ্টকাটসমূহ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"কীব\'ৰ্ডৰ সজ্জা সলনি কৰক"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"অথবা"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"সন্ধান কৰা প্ৰশ্ন মচক"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"শ্বৰ্টকাট"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"সন্ধানৰ শ্বৰ্টকাট"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"কোনো শ্বৰ্টকাট বিচাৰি পোৱা নাই"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ইনপুট"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"এপ্ খোলক"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"বৰ্তমানৰ এপ্"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"সন্ধানৰ ফলাফল দেখুৱাই থকা হৈছে"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"ছিষ্টেমৰ শ্বৰ্টকাট দেখুৱাই থকা হৈছে"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ইনপুট শ্বৰ্টকাট দেখুৱাই থকা হৈছে"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"এনে শ্বৰ্টকাট দেখুৱাই থকা হৈছে যিয়ে এপ্সমূহ খোলে"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"বৰ্তমানৰ এপ্টোৰ বাবে শ্বৰ্টকাট দেখুৱাই থকা হৈছে"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"জাননী চাওক"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"স্ক্ৰীনশ্বট লওক"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"শ্বৰ্টকাট দেখুৱাওক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index b2ee45529680..3603135e4a5e 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Düymə <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Əsas səhifə"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yuxarı ox"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ox"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ox"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ox"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Mərkəz"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluq"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Bildirişlər"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Klaviatura qısa yolları"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatura düzümünü dəyişin"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"və ya"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Axtarış sorğusunu silin"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Qısayollar"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Qısayollar axtarın"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Qısayol tapılmadı"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Daxiletmə"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Açıq tətbiqlər"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Cari tətbiq"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Axtarış nəticələri göstərilir"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Sistem qısayolları göstərilir"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Daxiletmə qısayolları göstərilir"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Tətbiqləri açan qısayollar göstərilir"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Cari tətbiq üçün qısayollar göstərilir"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Bildirişlərə baxın"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Skrinşot çəkin"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Qısayolları göstərin"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index ce734dc7b9fc..f26a9c573298 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Taster Početna"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Taster Nazad"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadole"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica nalevo"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica nadesno"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Taster sa centralnom strelicom"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Razmak"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Obaveštenja"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Tasterske prečice"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Promeni raspored tastature"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Obriši upit za pretragu"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečice"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretražite prečice"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nisu pronađene prečice"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Unos"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvaranje aplik"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuelna aplik"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Prikazuju se rezultati pretrage"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Prikazuju se sistemske prečice"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Prikazuju se prečice za umetanje"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Prikazuju se prečice za otvaranje aplikacija"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Prikazuju se prečice za aktuelnu aplikaciju"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaži obaveštenja"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Napravi snimak ekrana"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaži prečice"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 74e46fc3f154..c3d1d0a47b1d 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрэлка ўверх"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрэлка ўніз"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрэлка ўлева"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрэлка ўправа"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Цэнтр"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Прабел"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Апавяшчэнні"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Спалучэнні клавіш"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Пераключыць раскладку клавіятуры"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"або"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Ачысціць пошукавы запыт"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Ярлыкі"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пошук ярлыкоў"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ярлыкі не знойдзены"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Увод"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Адкрытыя праграмы"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Бягучая праграма"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Паказаны вынікі пошуку"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Паказаны сістэмныя спалучэнні клавіш"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Паказаны спалучэнні клавіш, звязаныя з уводам"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Паказаны спалучэнні клавіш для адкрыцця праграм"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Паказаны спалучэнні клавіш для бягучай праграмы"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Праглядзець апавяшчэнні"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Зрабіць здымак экрана"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Паказаць спалучэнні клавіш"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 7a00ded88cc9..e3eb1e0a7b83 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Бутон „<xliff:g id="NAME">%1$s</xliff:g>“"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Начало"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка за нагоре"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка за надолу"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка за наляво"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка за надясно"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Център"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Интервал"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Известия"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Клавишни комбинации"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Превкл. на клавиат. подредба"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Изчистване на заявката за търсене"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Клавишни комбинации"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Търсете комбинации"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Няма клавишни комбинации"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Въвеждане"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Отворени прил."</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Текущо прилож."</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Показани са резултатите от търсенето"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Показани са системните преки пътища"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Показани са преките пътища за въвеждане"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Показани са преките пътища, които отварят приложения"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Показани са преките пътища за текущото приложение"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Преглед на известията"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Създаване на екранна снимка"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Показване на клавишните комбинации"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 9954fbd4d5eb..6972379502e4 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"হোম"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ফিরুন"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ঊর্ধমুখী তীরচিহ্ন"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"নিম্নমুখী তীরচিহ্ন"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাঁদিকের তীরচিহ্ন"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ডানদিকের তীরচিহ্ন"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"কেন্দ্র"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"বিজ্ঞপ্তি"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"কীবোর্ড শর্টকাট"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"কীবোর্ড লে-আউট পাল্টান"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"অথবা"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"সার্চ কোয়েরি মুছুন"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"শর্টকাট"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"শর্টকাট সার্চ করুন"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"কোনও শর্টকার্ট পাওয়া যায়নি"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ইনপুট"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"খুলে রাখা অ্যাপ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"বর্তমান অ্যাপ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"সার্চ ফলাফল দেখানো হচ্ছে"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"সিস্টেম শর্টকাট দেখানো হচ্ছে"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ইনপুট শর্টকাট দেখানো হচ্ছে"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"যে শর্টকাট ব্যবহার করলে অ্যাপ খুলবে, সেগুলি দেখানো হচ্ছে"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"বর্তমান অ্যাপের জন্য শর্টকাট দেখানো হচ্ছে"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"বিজ্ঞপ্তি দেখুন"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"স্ক্রিনশট নিন"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"শর্টকাট দেখুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 041e3b6a78f0..939c2b2b6d1b 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Tipka za početak"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Nazad"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadolje"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica ulijevo"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica udesno"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Tipka za razmak"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Obavještenja"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Prečice tastature"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Zamijeni raspored tastature"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Brisanje upita za pretraživanje"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečice"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretraživanje prečica"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nisu pronađene prečice"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Unos"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvorene aplik."</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Trenutna aplik."</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Prikazivanje rezultata pretraživanja"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Prikazivanje prečica za sistem"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Prikazivanje prečica za unos"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Prikazivanje prečica koje otvaraju aplikacije"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Prikazivanje prečica za trenutnu aplikaciju"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaz obavještenja"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Pravljenje snimka ekrana"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaz prečica"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index eb652a16cca0..1a95e2a9e6b5 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inici"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Enrere"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Fletxa amunt"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Fletxa avall"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Fletxa esquerra"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Fletxa dreta"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espai"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificacions"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Tecles de drecera"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Canvia disposició de teclat"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Esborra la consulta de cerca"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Dreceres"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cerca dreceres"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No s\'ha trobat cap drecera"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Obre aplicacions"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aplicació actual"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"S\'estan mostrant els resultats de la cerca"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"S\'estan mostrant les dreceres del sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"S\'estan mostrant les dreceres d\'entrada"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"S\'estan mostrant les dreceres que obren aplicacions"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"S\'estan mostrant les dreceres per a l\'aplicació actual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Mostra les notificacions"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Fes una captura de pantalla"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostra les dreceres"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 1cfb005b3140..f97a754bf6d7 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knap"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbage"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pil op"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pil ned"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Højrepil"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midtertast"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Mellemrumstast"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifikationer"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Tastaturgenveje"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Skift tastaturlayout"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Ryd søgeforespørgsel"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Genveje"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Søg efter genveje"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ingen genveje blev fundet"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Åbn apps"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuel app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Viser søgeresultater"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Viser systemgenveje"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Viser tastaturgenveje"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Viser genveje til at åbne apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Viser genveje til den aktuelle app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Se notifikationer"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Tag et screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Vis genveje"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 30b03b21a9fb..937fe6712bd3 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifications"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Keyboard shortcuts"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Open apps"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Current app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Showing search results"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Showing system shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Showing input shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Showing shortcuts that open apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Showing shortcuts for the current app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"View notifications"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Take screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Show shortcuts"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 30b03b21a9fb..937fe6712bd3 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifications"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Keyboard shortcuts"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Open apps"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Current app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Showing search results"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Showing system shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Showing input shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Showing shortcuts that open apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Showing shortcuts for the current app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"View notifications"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Take screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Show shortcuts"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 30b03b21a9fb..937fe6712bd3 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifications"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Keyboard shortcuts"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Switch keyboard layout"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"or"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Clear search query"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shortcuts"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Search shortcuts"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"No shortcuts found"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Open apps"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Current app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Showing search results"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Showing system shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Showing input shortcuts"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Showing shortcuts that open apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Showing shortcuts for the current app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"View notifications"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Take screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Show shortcuts"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index f64238656d03..329b0f04b897 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha hacia la izquierda"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha hacia la derecha"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulador"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificaciones"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Ver combinaciones de teclas"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambiar diseño del teclado"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Borrar la consulta de búsqueda"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Combinaciones de teclas"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar combinaciones de teclas"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ninguna encontrada"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apps abiertas"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"App en uso"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Mostrando resultados de la búsqueda"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Mostrando combinaciones de teclas del sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Mostrando combinaciones de teclas de entrada"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Mostrando combinaciones de teclas que abren aplicaciones"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Mostrando combinaciones de teclas de la aplicación actual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Ver notificaciones"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Hacer captura de pantalla"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar accesos directos"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 8e9616fc2948..48bf1d508216 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nupp <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Avakuva"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tagasi"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ülesnool"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Allanool"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasaknool"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Paremnool"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskele"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Tühik"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Märguanded"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Klaviatuuri otseteed"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatuuripaigutuse vahetus"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"või"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Otsingupäringu tühjendamine"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Otseteed"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Otseteede otsing"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Otseteid ei leitud"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Sisend"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Rakenduste avamine"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Praegune rakendus"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Kuvatakse otsingutulemused"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Kuvatakse süsteemi otseteed"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Kuvatakse sisendi otseteed"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Kuvatakse rakenduste avamise otseteed"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Kuvatakse praeguse rakenduse otseteed"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Märguannete vaatamine"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ekraanipildi jäädvustamine"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Otseteede kuvamine"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index fe04218bcbd6..e48115728de8 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> botoia"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Hasiera"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atzera"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Gora egiteko gezi-tekla"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Behera egiteko gezi-tekla"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ezkerrera egiteko gezi-tekla"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Eskuinera egiteko gezi-tekla"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Erdiratu"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Zuriunea"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Lasterbideak"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Aldatu tekl. diseinua"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"edo"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Garbitu bilaketa-kontsulta"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Lasterbideak"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Bilatu lasterbideak"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ez da aurkitu lasterbiderik"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Sarrera"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Irekitako aplikazioak"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Uneko aplikazioa"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Bilaketa-emaitzak daude ikusgai"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Sistemaren lasterbideak daude ikusgai"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Informazioa sartzeko lasterbideak daude ikusgai"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Aplikazioak irekitzen dituzten lasterbideak daude ikusgai"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Oraingo aplikaziorako lasterbideak daude ikusgai"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Ikusi jakinarazpenak"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Atera pantaila-argazki bat"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Erakutsi lasterbideak"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 2f1783523919..fa5cce2b6d41 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"دکمه <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ابتدا"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"برگشت"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"پیکان رو بهبالا"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"پیکان روبهپایین"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"پیکان چپ"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"پیکان راست"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"مرکز"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"اعلانها"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"میانبرهای صفحهکلید"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر جانمایی صفحهکلید"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"یا"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"پاک کردن پُرسمان جستجو"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"میانبرها"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"جستجوی میانبرها"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"میانبری پیدا نشد"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ورودی"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"باز کردن برنامهها"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"برنامه فعلی"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"درحال نمایش نتایج جستجو"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"درحال نمایش میانبرهای سیستم"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"درحال نمایش میانبرهای ورودی"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"درحال نمایش میانبرهای باز کردن برنامهها"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"درحال نمایش میانبرهای برنامه کنونی"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"مشاهده اعلانها"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"گرفتن نماگرفت"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"نمایش میانبرها"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index d1bd05be11ad..aebcd58289ef 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Painike <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Takaisin"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ylänuoli"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Alanuoli"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasen nuoli"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oikea nuoli"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskelle"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Välilyönti"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Ilmoitukset"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Pikanäppäimet"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Vaihda näppäimistöasettelu"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"tai"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Tyhjennä hakulauseke"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pikanäppäimet"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Hae pikanäppäimiä"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Pikanäppäimiä ei löytynyt"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Syöttötapa"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Avoimet"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Sovelluslista"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Näytetään hakutuloksia"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Näytetään järjestelmän pikanäppäimiä"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Näytetään pikanäppäimiä"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Näytetään sovellusten avaamiseen tarkoitettuja pikanäppäimiä"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Näytetään sovelluksen pikanäppäimiä"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Katso ilmoitukset"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ota kuvakaappaus"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Näytä pikakuvakkeet"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index bcf0e96c1d60..9440689fdc89 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Précédent"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flèche vers le haut"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flèche vers le bas"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flèche vers la gauche"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flèche vers la droite"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrer"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espace"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifications"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Raccourcis clavier"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Changer la disposition du clavier"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Effacez la requête de recherche"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Raccourcis"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Recherchez des raccourcis"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Aucun raccourci trouvé"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrée"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ouvrir applis"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Appli actuelle"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Affichage des résultats de recherche en cours…"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Affichage des raccourcis du système en cours…"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Affichage des raccourcis d\'entrée en cours…"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Affichage des raccourcis qui ouvrent les applications en cours…"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Affichage des raccourcis de l\'application actuelle en cours…"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Afficher les notifications"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Prendre une capture d\'écran"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Afficher les raccourcis"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 716fcf23f485..38daccbf536c 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Volver"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Frecha arriba"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Frecha abaixo"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Frecha esquerda"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Frecha dereita"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Espazo"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificacións"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atallos de teclado"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambiar deseño do teclado"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Borrar a busca"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atallos"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Buscar atallos"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Non se atoparon atallos"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Abrir aplicacións"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"App actual"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Mostrando resultados da busca"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Mostrando atallos do sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Mostrando atallos de introdución de datos"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Mostrando atallos que abren aplicacións"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Mostrando atallos da aplicación actual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Ver notificacións"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Facer captura de pantalla"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar atallos"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 837986546e78..be5328b942d3 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"બટન <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ઉપરની ઍરો કી"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"નીચેની ઍરો કી"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ડાબી ઍરો કી"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"જમણી ઍરો કી"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"નોટિફિકેશન"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"કીબોર્ડ શૉર્ટકટ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"કીબોર્ડ લેઆઉટ સ્વિચ કરો"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"અથવા"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"શોધ ક્વેરી સાફ કરો"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"શૉર્ટકટ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"શૉર્ટકટ શોધો"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"કોઈ શૉર્ટકટ મળ્યો નથી"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ઇનપુટ"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ઍપ ખોલો"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"હાલની ઍપ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"શોધ પરિણામો બતાવી રહ્યાં છીએ"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"સિસ્ટમના શૉર્ટકટ બતાવી રહ્યાં છીએ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ઇનપુટ માટે શૉર્ટકટ બતાવી રહ્યાં છીએ"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ઍપ ખોલનારા શૉર્ટકટ બતાવી રહ્યાં છીએ"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"હાલની ઍપ માટે શૉર્ટકટ બતાવી રહ્યાં છીએ"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"નોટિફિકેશન જુઓ"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"સ્ક્રીનશૉટ લો"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"શૉર્ટકટ બતાવો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index ab01d2bd79aa..da72681388a6 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटन <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप ऐरो"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन ऐरो"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ़्ट ऐरो"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट ऐरो"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्य तीर"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"सूचनाएं"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"कीबोर्ड शॉर्टकट"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"कीबोर्ड लेआउट बदलें"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"या"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"सर्च क्वेरी साफ़ करें"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"शॉर्टकट"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"शॉर्टकट खोजें"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"कोई शॉर्टकट नहीं मिला"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"इनपुट"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"खुले हुए ऐप्लिकेशन"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"मौजूदा ऐप्लिकेशन"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"खोज के नतीजे दिखाए जा रहे हैं"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"सिस्टम के शॉर्टकट दिखाए जा रहे हैं"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"इनपुट के लिए शॉर्टकट दिखाए जा रहे हैं"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ऐसे शॉर्टकट दिखाए जा रहे हैं जिनसे ऐप्लिकेशन खोले जा सकें"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"मौजूदा ऐप्लिकेशन के लिए शॉर्टकट दिखाए जा रहे हैं"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचनाएं देखें"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रीनशॉट लें"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"शॉर्टकट दिखाएं"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 7584e8ae6e32..065bc281323f 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tipka <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Početak"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Natrag"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica prema gore"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica prema dolje"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica lijevo"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica desno"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Razmaknica"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Obavijesti"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Tipkovni prečaci"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Promjena rasporeda tipkovnice"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Brisanje upita za pretraživanje"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Prečaci"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretražite prečace"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nema nijednog prečaca"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Unos"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvaranje aplikacija"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Trenutačna aplikacija"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Prikazuju se rezultati pretraživanja"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Prikazuju se prečaci sustava"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Prikazuju se prečaci za unos"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Prikazuju se prečaci koji otvaraju aplikacije"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Prikazuju se prečaci za trenutačnu aplikaciju"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Prikaži obavijesti"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Snimi zaslon"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaži prečace"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 85fe45525ea5..2a08e1599f7a 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> gomb"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Kezdőképernyő"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Vissza"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Felfelé nyíl"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lefelé nyíl"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Balra nyíl"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Jobbra nyíl"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Középre"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Szóköz"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Értesítések"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Billentyűkódok"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Billentyűzetkiosztás váltása"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"vagy"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Keresőkifejezés törlése"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Billentyűparancsok"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Billentyűparancs keresése"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nincs billentyűparancs"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Bevitel"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Futó appok"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuális app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Keresési találatok megjelenítve"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Rendszer-billentyűparancsok megjelenítve"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Beviteli billentyűparancsok megjelenítve"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Alkalmazásokat megnyitó billentyűparancsok megjelenítve"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"A jelenlegi alkalmazáshoz tartozó billentyűparancsok megjelenítve"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Értesítések megtekintése"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Képernyőkép készítése"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Parancsikonok megjelenítése"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 5be6a874af0e..58162647e6f6 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> կոճակ"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Գլխավոր էջ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Հետ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Վերև սլաք"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ներքև սլաք"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ձախ սլաք"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Աջ սլաք"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Կենտրոն"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Բացատ"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Ծանուցումներ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Ստեղնային դյուրանցումներ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Դասավորության փոխարկում"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"կամ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Մաքրել որոնման դաշտը"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Դյուրանցումներ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Դյուրանցումների որոնում"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Դյուրանցումներ չեն գտնվել"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Ներածում"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Բաց հավելվածներ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Այս հավելվածը"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Ցուցադրվում են որոնման արդյունքները"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Ցուցադրվում են համակարգի դյուրանցումները"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Ցուցադրվում են մուտքագրման դյուրանցումները"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Ցուցադրվում են հավելվածներ բացող դյուրանցումները"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Ցուցադրվում են ընթացիկ հավելվածի դյուրանցումները"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Դիտել ծանուցումները"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Սքրինշոթ անել"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Ցույց տալ դյուրանցումները"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 8d372dc16f6b..428bd386af94 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tombol <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Panah atas"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Panah bawah"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Panah kiri"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Panah kanan"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifikasi"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Pintasan keyboard"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Ganti tata letak keyboard"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"atau"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Hapus kueri penelusuran"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pintasan"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pintasan penelusuran"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tidak ditemukan pintasan"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Aplikasi yang terbuka"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aplikasi saat ini"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Menampilkan hasil penelusuran"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Menampilkan pintasan sistem"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Menampilkan pintasan input"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Menampilkan pintasan yang membuka aplikasi"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Menampilkan pintasan untuk aplikasi saat ini"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Lihat notifikasi"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ambil screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tampilkan pintasan"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index aad1e56d88bc..d4847e5f72ff 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home page"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Indietro"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Freccia su"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Freccia giù"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Freccia sinistra"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Freccia destra"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Al centro"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spazio"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notifiche"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Scorciatoie da tastiera"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Cambia layout della tastiera"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"oppure"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Cancella la query di ricerca"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Scorciatoie"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cerca scorciatoie"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Scorciatoie non trovate"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Inserimento"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"App aperte"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"App corrente"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Visualizzazione dei risultati di ricerca"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Visualizzazione delle scorciatoie di sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Visualizzazione delle scorciatoie degli ingressi"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Visualizzazione delle scorciatoie che aprono le app"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Visualizzazione delle scorciatoie per l\'app corrente"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Visualizza notifiche"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Acquisisci screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostra scorciatoie"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index c8a0a540fb6a..8e1fd1ca541b 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ボタン"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"戻る"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"上矢印"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"下矢印"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"左矢印"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"右矢印"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"通知"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"キーボード ショートカット"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"キーボード レイアウトの切り替え"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"または"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"検索クエリをクリア"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ショートカット"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ショートカットの検索"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ショートカットがありません"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"入力"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"開いているアプリ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"現在のアプリ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"検索結果を表示しています"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"システムのショートカットを表示しています"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"入力用のショートカットを表示しています"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"アプリを開くショートカットを表示しています"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"現在のアプリのショートカットを表示しています"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"お知らせを表示する"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"スクリーンショットを撮る"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ショートカットを表示する"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 2831cf941861..20eeb76f3140 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Артқа"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Жоғары бағыт пернесі"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Төмен бағыт пернесі"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Сол бағыт пернесі"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оң бағыт пернесі"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Орталық"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Бос орын"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Хабарландырулар"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Перне тіркесімдері"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Пернетақта форматын ауыстыру"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"немесе"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Іздеу сұрауын өшіру"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Перне тіркесімдері"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Перне тіркесімдерін іздеу"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Перне тіркесімдері табылмады."</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Енгізу"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ашылған қолданбалар"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Ағымдағы қолданба"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Іздеу нәтижелерін көрсетеді."</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Жүйе жылдам пәрмендерін көрсетеді."</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Енгізу жылдам пәрмендерін көрсетеді."</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Қолданбаларды ашатын жылдам пәрмендерді көрсетеді."</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Қазіргі қолданбаға арналған жылдам пәрмендер көрсетіледі."</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Хабарландыруларды көру"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Скриншот жасау"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Жылдам пәрмендерді көрсету"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index aff6ef7ae59c..2167443a9c20 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ព្រួញឡើងលើ"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ព្រួញចុះក្រោម"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ព្រួញទៅឆ្វេង"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ព្រួញទៅស្ដាំ"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"ការជូនដំណឹង"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"ផ្លូវកាត់ក្ដារចុច"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ប្ដូរប្លង់ក្ដារចុច"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ឬ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"សម្អាតសំណួរស្វែងរក"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ផ្លូវកាត់"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ស្វែងរកផ្លូវកាត់"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"រកផ្លូវកាត់មិនឃើញទេ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"បញ្ចូល"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"កម្មវិធីដែលបើក"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"កម្មវិធីបច្ចុប្បន្ន"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"កំពុងបង្ហាញលទ្ធផលស្វែងរក"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"កំពុងបង្ហាញផ្លូវកាត់ប្រព័ន្ធ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"កំពុងបង្ហាញផ្លូវកាត់បញ្ចូល"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"កំពុងបង្ហាញផ្លូវកាត់ដែលបើកកម្មវិធី"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"កំពុងបង្ហាញផ្លូវកាត់សម្រាប់កម្មវិធីបច្ចុប្បន្ន"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"មើលការជូនដំណឹង"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ថតរូបអេក្រង់"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"បង្ហាញផ្លូវកាត់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 5be72d837d00..1118a19f402c 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ಬಟನ್"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ಹಿಂದೆ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ಅಪ್ ಆ್ಯರೋ"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ಡೌನ್ ಆ್ಯರೋ"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ಲೆಫ್ಟ್ ಆ್ಯರೋ"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ರೈಟ್ ಆ್ಯರೋ"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ಮಧ್ಯ"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"ಸ್ಪೇಸ್"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"ನೋಟಿಫಿಕೇಶನ್ಗಳು"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ಕೀಬೋರ್ಡ್ ಲೇಔಟ್ ಬದಲಾಯಿಸಿ"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ಅಥವಾ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ಹುಡುಕಾಟದ ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸಿ"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಹುಡುಕಿ"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ಯಾವುದೇ ಶಾರ್ಟ್ಕಟ್ಗಳಿಲ್ಲ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ಇನ್ಪುಟ್"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ಆ್ಯಪ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"ಪ್ರಸ್ತುತ ಆ್ಯಪ್"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"ಸಿಸ್ಟಂ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ಇನ್ಪುಟ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ಆ್ಯಪ್ಗಳನ್ನು ತೆರೆಯುವ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"ಪ್ರಸ್ತುತ ಆ್ಯಪ್ಗಾಗಿ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೋರಿಸಲಾಗುತ್ತಿದೆ"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ತೋರಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 71c0a3526154..0fa92665c825 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"뒤로"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"위쪽 화살표"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"아래쪽 화살표"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"왼쪽 화살표"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"오른쪽 화살표"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"중앙"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"알림"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"단축키"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"키보드 레이아웃 전환"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"또는"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"검색어 삭제"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"단축키"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"단축키 검색"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"단축키 없음"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"입력"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"열린 앱"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"현재 앱"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"검색 결과 표시"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"시스템 바로가기 표시"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"입력 바로가기 표시"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"앱 열기 바로가기 표시"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"현재 앱 바로가기 표시"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"알림 보기"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"스크린샷 촬영"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"단축키 표시"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 6276160ff67a..06c0f99e48f6 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> баскычы"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Башкы бет"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Артка"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Өйдө жебе"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ылдый жебе"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Солго жебе"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оңго жебе"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ортолотуу"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Боштук"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Билдирмелер"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Ыкчам баскычтар"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Баскычтоп калыбын которуштуруу"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"же"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Изделген суроону тазалоо"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Ыкчам баскычтар"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Ыкчам баскычтарды издөө"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ыкчам баскычтар жок"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Киргизүү"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ачык колдон-лор"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Учурдагы кол-мо"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Табылган нерселер көрсөтүлүүдө"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Тутумдун ыкчам баскычтары көрсөтүлүүдө"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Киргизүү үчүн ыкчам баскычтар көрсөтүлүүдө"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Колдонмолорду ачкан ыкчам баскычтар көрсөтүлүүдө"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Учурдагы колдонмонун ыкчам баскычтары көрсөтүлүүдө"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Билдирмелерди көрүү"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Скриншот тартуу"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Ыкчам баскычтарды көрсөтүү"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 5da52a5d97c2..f88de0c99dee 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ປຸ່ມ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ກັບຄືນ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ລູກສອນຂຶ້ນ"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ລູກສອນລົງ"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ລູກສອນຊ້າຍ"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ລູກສອນຂວາ"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ເຄິ່ງກາງ"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"ການແຈ້ງເຕືອນ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"ປຸ່ມລັດແປ້ນພິມ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ສະຫຼັບແປ້ນພິມ"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ຫຼື"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ລຶບລ້າງຄຳຊອກຫາ"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ທາງລັດ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ທາງລັດການຊອກຫາ"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ບໍ່ພົບທາງລັດ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ການປ້ອນຂໍ້ມູນ"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ແອັບທີ່ເປີດຢູ່"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"ແອັບປັດຈຸບັນ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"ສະແດງຜົນການຊອກຫາ"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"ສະແດງທາງລັດລະບົບ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ສະແດງທາງລັດອິນພຸດ"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ສະແດງທາງລັດທີ່ເປີດແອັບ"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"ສະແດງທາງລັດສຳລັບແອັບປັດຈຸບັນ"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"ເບິ່ງການແຈ້ງເຕືອນ"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ຖ່າຍຮູບໜ້າຈໍ"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ສະແດງທາງລັດ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 34c06bd89d99..de8756cd6c46 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atgal"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Rodyklė aukštyn"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Rodyklė žemyn"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Rodyklė kairėn"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Rodyklė dešinėn"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centras"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Tarpas"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Pranešimai"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Spartieji klavišai"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Perjungti klaviat. išdėstymą"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"arba"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Išvalyti paieškos užklausą"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Spartieji klavišai"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Ieškoti sparčiųjų klavišų"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Sparčiųjų klavišų nerasta"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Įvestis"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Atidar. progr."</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Esama programa"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Rodomi paieškos rezultatai"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Rodomi sistemos spartieji klavišai"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Rodomi įvesties spartieji klavišai"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Rodomi programas atidarantys spartieji klavišai"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Rodomi dabartinės programos spartieji klavišai"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Peržiūrėti pranešimus"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Padaryti ekrano kopiją"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Rodyti sparčiuosius klavišus"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 66933bc2f112..258423034e67 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Poga <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Sākumvietas taustiņš"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Atpakaļ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Augšupvērstā bultiņa"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lejupvērstā bultiņa"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Kreisā bultiņa"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Labā bultiņa"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrā"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Atstarpe"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Paziņojumi"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Īsinājumtaustiņi"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Mainīt tastatūras izkārtojumu"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"vai"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Notīrīt meklēšanas vaicājumu"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Īsinājumtaustiņi"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Meklēt īsinājumtaustiņus"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nav atrasti"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Ievade"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Atvērtās"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Pašreizējā"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Tiek rādīti meklēšanas rezultāti"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Tiek rādīti sistēmas īsinājumtaustiņi"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Tiek rādīti ievades īsinājumtaustiņi"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Tiek rādīti īsinājumtaustiņi lietotņu atvēršanai"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Tiek rādīti īsinājumtaustiņi pašreizējai lietotnei"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Skatīt paziņojumus"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Izveidot ekrānuzņēmumu"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Rādīt īsinājumtaustiņus"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 44f6e0c0e772..f0036b84421c 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Копче <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home-копче"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка нагоре"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка надолу"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка налево"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка надесно"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центар"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Известувања"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Кратенки на тастатурата"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Промени јазик на тастатура"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Бришење поим за пребарување"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Кратенки"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пребарувајте кратенки"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Не се пронајдени кратенки"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Внесување"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Отворени аплик."</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Тековна аплик."</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Се прикажуваат резултати од пребарувањето"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Се прикажуваат системски кратенки"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Се прикажуваат кратенки за внесување"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Се прикажуваат кратенки за отворање апликации"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Се прикажуваат кратенки за тековната апликација"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Прегледајте ги известувањата"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Направете слика од екранот"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Прикажи кратенки"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 4644fa5e5886..dadf2ce32595 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ഹോം"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ബാക്ക്"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"മുകളിലേക്കുള്ള അമ്പടയാളം"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"താഴേക്കുള്ള അമ്പടയാളം"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ഇടത് അമ്പടയാളം"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"വലത് അമ്പടയാളം"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"മധ്യം"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"TAB"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"സ്പെയ്സ്"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"അറിയിപ്പുകൾ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"കീബോർഡ് കുറുക്കുവഴികൾ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"കീബോർഡ് ലേഔട്ട് മാറുക"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"അല്ലെങ്കിൽ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"തിരയൽ ചോദ്യം മായ്ക്കുക"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"കുറുക്കുവഴികൾ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"കുറുക്കുവഴികൾ തിരയുക"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"കുറുക്കുവഴി കണ്ടെത്തിയില്ല"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ഇൻപുട്ട്"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"തുറന്ന ആപ്പുകൾ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"നിലവിലെ ആപ്പ്"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"തിരയൽ ഫലങ്ങൾ കാണിക്കുന്നു"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"സിസ്റ്റം കുറുക്കുവഴികൾ കാണിക്കുന്നു"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ഇൻപുട്ട് കുറുക്കുവഴികൾ കാണിക്കുന്നു"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ആപ്പുകൾ തുറക്കുന്ന കുറുക്കുവഴികൾ കാണിക്കുന്നു"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"നിലവിലെ ആപ്പിനുള്ള കുറുക്കുവഴികൾ കാണിക്കുന്നു"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"അറിയിപ്പുകൾ കാണുക"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"സ്ക്രീൻഷോട്ട് എടുക്കുക"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"കുറുക്കുവഴികൾ കാണിക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 217eeb1a3192..9c73642c9537 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> товчлуур"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Нүүр хуудас"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Буцах"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Дээш сум"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Доош сум"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Зүүн сум"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Баруун сум"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Гол хэсэг"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Зай"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Мэдэгдэл"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Гарын товчлол"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Гарын бүдүүвч рүү сэлгэх"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"эсвэл"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Хайлтын асуулгыг арилгах"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Товчлолууд"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Товчлолууд хайх"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Ямар ч товчлол олдсонгүй"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Оролт"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Нээлттэй аппууд"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Одоогийн апп"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Хайлтын илэрцүүдийг харуулж байна"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Системийн товчлолуудыг харуулж байна"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Оролтын товчлолуудыг харуулж байна"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Аппуудыг нээдэг товчлолуудыг харуулж байна"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Одоогийн аппад товчлолууд харуулж байна"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Мэдэгдлийг харах"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Дэлгэцийн агшныг авах"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Товчлол харуулах"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index f7688810007d..27f1b8ca7070 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटण <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"परत"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप अॅरो"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन अॅरो"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट अॅरो"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट अॅरो"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्यवर्ती"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"सूचना"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"कीबोर्ड शॉर्टकट"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"कीबोर्ड लेआउट स्विच करा"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"किंवा"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"शोध क्वेरी साफ करा"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"शॉर्टकट"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"शॉर्टकट शोधा"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"एकही शॉर्टकट आढळला नाहीत"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"इनपुट"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ॲप्स उघडा"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"सध्याचे अॅप"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"शोध परिणाम दाखवत आहे"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"सिस्टीम शॉर्टकट दाखवत आहे"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"इनपुट शॉर्टकट दाखवत आहे"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ॲप्स उघडणारे शॉर्टकट दाखवत आहे"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"सद्य अॅपसाठी शॉर्टकट दाखवत आहे"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचना पहा"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रीनशॉट घ्या"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"शॉर्टकट दाखवा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index db538904255c..704cd66d38be 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butang <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Skrin Utama"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Kembali"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Anak panah ke atas"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Anak panah ke bawah"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Anak panah ke kiri"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Anak panah ke kanan"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Tengah"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Pemberitahuan"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Pintasan Papan Kekunci"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Tukar reka letak papan kekunci"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"atau"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Kosongkan pertanyaan carian"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Pintasan"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Cari pintasan"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tiada pintasan ditemukan"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apl yang dibuka"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Apl semasa"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Memaparkan hasil carian"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Memaparkan pintasan sistem"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Memaparkan pintasan input"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Memaparkan pintasan yang membuka apl"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Memaparkan pintasan untuk apl semasa"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Lihat pemberitahuan"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ambil tangkapan skrin"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tunjukkan pintasan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 6221ca1c62fe..333165995372 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ပင်မ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"နောက်သို့"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"အပေါ်ညွှန်မြား"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"အောက်ညွှန်မြား"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ဘယ်ညွှန်မြား"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ညာညွှန်မြား"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ဌာန"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"အကြောင်းကြားချက်များ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"ကီးဘုတ် ဖြတ်လမ်းများ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ကီးဘုတ်အပြင်အဆင် ပြောင်းခြင်း"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"သို့မဟုတ်"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ရှာဖွေစာလုံး ရှင်းထုတ်ရန်"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ဖြတ်လမ်းလင့်ခ်များ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ဖြတ်လမ်းလင့်ခ်များ ရှာပါ"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ဖြတ်လမ်းလင့်ခ် မတွေ့ပါ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"စာရိုက်ခြင်း"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ဖွင့်ထားသောအက်ပ်"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"လက်ရှိအက်ပ်"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"ရှာဖွေမှုရလဒ်များ ပြထားသည်"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"စနစ်ဖြတ်လမ်းလင့်ခ်များ ပြထားသည်"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ထည့်သွင်းသည့် ဖြတ်လမ်းလင့်ခ်များ ပြထားသည်"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"အက်ပ်ဖွင့်သည့် ဖြတ်လမ်းလင့်ခ်များ ပြထားသည်"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"လက်ရှိအက်ပ်အတွက် ဖြတ်လမ်းလင့်ခ်များ ပြထားသည်"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"အကြောင်းကြားချက်များ ကြည့်ရန်"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ဖန်သားပြင်ဓာတ်ပုံ ရိုက်ရန်"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ဖြတ်လမ်းလင့်ခ်များ ပြပါ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 9fc3625203ae..e0761027cc74 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knappen"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Startskjerm"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbake"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppoverpil"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedoverpil"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Høyrepil"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midttasten"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Mellomrom"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Varsler"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Hurtigtaster"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Bytt tastaturoppsett"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Fjern søket"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Hurtigtaster"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Søk etter hurtigtaster"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Fant ingen hurtigtaster"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Inndata"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Åpne apper"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktiv app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Viser søkeresultater"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Viser systemsnarveier"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Viser inndatasnarveier"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Viser snarveier som åpner apper"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Viser snarveier for den gjeldende appen"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Se varsler"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ta skjermdump"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Vis snarveier"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 79cb7037b5c8..1a0eacb9d01e 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> बटन"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"पछाडि"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप एरो"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन एरो"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट एरो"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट एरो"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"केन्द्र"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"स्पेस"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"सूचनाहरू"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"किबोर्ड सर्टकटहरू"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"किबोर्डको लेआउट बदल्नुहोस्"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"वा"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"किवर्ड हटाउनुहोस्"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"सर्टकटहरू"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"सर्टकटहरू खोज्नुहोस्"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"कुनै पनि सर्टकट भेटिएन"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"इनपुट"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"खोलिएका एपहरू"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"हालको एप"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"खोज परिणामहरू देखाइँदै छ"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"सिस्टमका सर्टकटहरू देखाइँदै छ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"इनपुट गर्ने सर्टकटहरू देखाइँदै छ"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"एपहरू खोल्ने सर्टकटहरू देखाइँदै छ"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"हालको एपका लागि सर्टकटहरू देखाइँदै छ"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"सूचनाहरू हेर्नुहोस्"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"स्क्रिनसट खिच्नुहोस्"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"सर्टकटहरू देखाइऊन्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 60ddb891ad5a..875447334174 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knop <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Terug"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pijl-omhoog"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pijl-omlaag"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pijl-links"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pijl-rechts"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midden"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spatiebalk"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Meldingen"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Sneltoetsen"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Toetsenbordindeling wisselen"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"of"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Zoekopdracht wissen"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Sneltoetsen"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sneltoetsen zoeken"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Geen sneltoetsen gevonden"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Invoer"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apps openen"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Huidige app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Zoekresultaten tonen"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Systeemsnelkoppelingen tonen"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Invoersnelkoppelingen tonen"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Snelkoppelingen tonen die apps openen"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Snelkoppelingen voor de huidige app tonen"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Meldingen bekijken"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Screenshot maken"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Snelkoppelingen tonen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index e02a5a72591b..8914d95ddaa6 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ବଟନ୍ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ଫେରନ୍ତୁ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ଅପ ତୀର କୀ"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ଡାଉନ ଆରୋ"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ବାମ ତୀର କୀ"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ଡାହାଣ ତୀର କୀ"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"କେନ୍ଦ୍ର"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"ସ୍ପେସ୍"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"ବିଜ୍ଞପ୍ତି"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"କୀ\'ବୋର୍ଡ୍ର ଲେଆଉଟ୍କୁ ବଦଳାନ୍ତୁ"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"କିମ୍ବା"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ସର୍ଚ୍ଚ କ୍ୱେରୀକୁ ଖାଲି କରନ୍ତୁ"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ସର୍ଟକଟଗୁଡ଼ିକ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ସର୍ଟକଟ ସର୍ଚ୍ଚ କରନ୍ତୁ"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"କୌଣସି ସର୍ଟକଟ ମିଳିଲା ନାହିଁ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ଇନପୁଟ କରନ୍ତୁ"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ଆପ୍ସ ଖୋଲନ୍ତୁ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"ବର୍ତ୍ତମାନର ଆପ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"ସର୍ଚ୍ଚ ଫଳାଫଳ ଦେଖାଯାଉଛି"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"ସିଷ୍ଟମ ସର୍ଟକଟ ଦେଖାଯାଉଛି"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ଇନପୁଟ ସର୍ଟକଟ ଦେଖାଯାଉଛି"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ଆପ୍ସକୁ ଖୋଲୁଥିବା ସର୍ଟକଟ ଦେଖାଯାଉଛି"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"ବର୍ତ୍ତମାନର ଆପ ପାଇଁ ସର୍ଟକଟ ଦେଖାଯାଉଛି"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଭ୍ୟୁ କରନ୍ତୁ"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ସ୍କ୍ରିନସଟ ନିଅନ୍ତୁ"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ସର୍ଟକଟଗୁଡ଼ିକ ଦେଖାନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 4ab6e8be44c7..e450b2d401aa 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ਉੱਪਰ ਤੀਰ"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ਹੇਠਾਂ ਤੀਰ"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ਖੱਬਾ ਤੀਰ"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ਸੱਜਾ ਤੀਰ"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"ਸੂਚਨਾਵਾਂ"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"ਕੀ-ਬੋਰਡ ਖਾਕਾ ਬਦਲੋ"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ਜਾਂ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ਖੋਜ ਪੁੱਛਗਿੱਛ ਕਲੀਅਰ ਕਰੋ"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ਸ਼ਾਰਟਕੱਟ"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਖੋਜੋ"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਨਹੀਂ ਮਿਲਿਆ"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ਇਨਪੁੱਟ"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ਐਪਾਂ ਖੋਲ੍ਹੋ"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"ਮੌਜੂਦਾ ਐਪ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"ਖੋਜ ਨਤੀਜੇ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"ਸਿਸਟਮ ਸ਼ਾਰਟਕੱਟ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ਇਨਪੁੱਟ ਸ਼ਾਰਟਕੱਟ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ਐਪਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਵਾਲੇ ਸ਼ਾਰਟਕੱਟ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"ਮੌਜੂਦਾ ਐਪ ਦੇ ਸ਼ਾਰਟਕੱਟ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"ਸੂਚਨਾਵਾਂ ਦੇਖੋ"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਲਓ"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ਸ਼ਾਰਟਕੱਟ ਦਿਖਾਓ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 3647bd418c95..2d064fa33c45 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Przycisk <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Wstecz"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strzałka w górę"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strzałka w dół"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strzałka w lewo"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strzałka w prawo"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do środka"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Spacja"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Powiadomienia"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Skróty klawiszowe"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Przełącz układ klawiatury"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"lub"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Wyczyść wyszukiwanie hasło"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Skróty"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Wyszukiwanie skrótów"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nie znaleziono skrótów"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Wprowadzanie"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otwieranie aplikacji"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Bieżąca aplikacja"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Pokazuję wyniki wyszukiwania"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Pokazuję skróty systemowe"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Pokazuję skróty dotyczące danych wejściowych"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Pokazuję skróty otwierające aplikacje"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Pokazuję skróty dotyczące bieżącej aplikacji"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Wyświetlanie powiadomień"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Robienie zrzutu ekranu"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Pokazywanie skrótów"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 329f40a5739d..320c6b2fa380 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -133,7 +133,7 @@ <string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string> <string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string> - <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string> + <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de QR code"</string> <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string> <string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string> <string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string> @@ -568,7 +568,7 @@ <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string> <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string> <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string> - <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de código QR"</string> + <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de QR code"</string> <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string> <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string> @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificações"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atalhos do teclado"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar layout do teclado"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atalhos"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apps abertos"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"App atual"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Mostrando resultados da pesquisa"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Mostrando atalhos do sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Mostrando atalhos de entrada"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Mostrando atalhos que abrem apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Mostrando atalhos do app atual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Mostrar as notificações"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Fazer uma captura de tela"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar atalhos"</string> @@ -1043,7 +1032,7 @@ <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string> - <string name="media_output_broadcasting_message" msgid="4150299923404886073">"As pessoas próximas com dispositivos Bluetooth compatíveis podem ler seu código QR ou usar o nome da transmissão e a senha para ouvir a transmissão."</string> + <string name="media_output_broadcasting_message" msgid="4150299923404886073">"As pessoas próximas com dispositivos Bluetooth compatíveis podem ler seu QR code ou usar o nome da transmissão e a senha para ouvir a transmissão."</string> <string name="media_output_broadcast_name" msgid="8786127091542624618">"Nome da transmissão"</string> <string name="media_output_broadcast_code" msgid="870795639644728542">"Senha"</string> <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Salvar"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 329f40a5739d..320c6b2fa380 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -133,7 +133,7 @@ <string name="accessibility_phone_button" msgid="4256353121703100427">"Telefone"</string> <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistência de voz"</string> <string name="accessibility_wallet_button" msgid="1458258783460555507">"Carteira"</string> - <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de código QR"</string> + <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Leitor de QR code"</string> <string name="accessibility_unlock_button" msgid="3613812140816244310">"Desbloqueado"</string> <string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string> <string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string> @@ -568,7 +568,7 @@ <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string> <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string> <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string> - <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de código QR"</string> + <string name="qr_code_scanner_title" msgid="1938155688725760702">"Leitor de QR code"</string> <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Atualizando"</string> <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string> @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificações"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Atalhos do teclado"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Alterar layout do teclado"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ou"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Limpar a consulta de pesquisa"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Atalhos"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Atalhos de pesquisa"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenhum atalho encontrado"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Entrada"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apps abertos"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"App atual"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Mostrando resultados da pesquisa"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Mostrando atalhos do sistema"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Mostrando atalhos de entrada"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Mostrando atalhos que abrem apps"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Mostrando atalhos do app atual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Mostrar as notificações"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Fazer uma captura de tela"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Mostrar atalhos"</string> @@ -1043,7 +1032,7 @@ <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Transmitir"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"As pessoas próximas a você com dispositivos Bluetooth compatíveis podem ouvir a mídia que você está transmitindo"</string> - <string name="media_output_broadcasting_message" msgid="4150299923404886073">"As pessoas próximas com dispositivos Bluetooth compatíveis podem ler seu código QR ou usar o nome da transmissão e a senha para ouvir a transmissão."</string> + <string name="media_output_broadcasting_message" msgid="4150299923404886073">"As pessoas próximas com dispositivos Bluetooth compatíveis podem ler seu QR code ou usar o nome da transmissão e a senha para ouvir a transmissão."</string> <string name="media_output_broadcast_name" msgid="8786127091542624618">"Nome da transmissão"</string> <string name="media_output_broadcast_code" msgid="870795639644728542">"Senha"</string> <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Salvar"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index ee3945df6365..5ea8f36228a5 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Главный экран"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка вверх"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка вниз"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка влево"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка вправо"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центральная стрелка"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Пробел"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Уведомления"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Быстрые клавиши"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Переключение раскладки"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Удалить поисковый запрос"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Сочетания клавиш"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Поиск сочетаний клавиш"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Нет сочетаний клавиш."</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Ввод"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Запущенные приложения"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Это приложение"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Показаны результаты поиска"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Показаны системные сочетания клавиш"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Показаны сочетания клавиш, связанные с вводом"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Показаны сочетания клавиш для открытия приложений"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Показаны сочетания клавиш для текущего приложения"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Посмотреть уведомления"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Сделать скриншот"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Показать сочетания клавиш"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index e03c7c0f1c2d..afd720831cab 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> බොත්තම"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home යතුර"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"ආපසු"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ඉහළ ඊතලය"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"පහළ ඊතලය"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"වම් ඊතලය"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"දකුණු ඊතලය"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"මැද"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"ඉඩ යතුර"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"දැනුම්දීම්"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"යතුරු පුවරු කෙටිමං"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"යතුරුපුවරු පිරිසැලසුම මාරු කරන්න"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"හෝ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"සෙවීම් විමසුම හිස් කරන්න"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"කෙටිමං"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"කෙටිමං සොයන්න"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"කෙටිමං හමු නොවුණි"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ආදානය"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"විවෘත යෙදුම්"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"වත්මන් යෙදුම"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"සෙවීම් ප්රතිඵල පෙන්වමින්"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"පද්ධති කෙටිමං පෙන්වමින්"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ආදාන කෙටිමං පෙන්වමින්"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"යෙදුම් විවෘත කරන කෙටිමං පෙන්වමින්"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"වත්මන් යෙදුම සඳහා කෙටිමං පෙන්වමින්"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"දැනුම්දීම් බලන්න"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"තිර රුව ගන්න"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"කෙටිමං පෙන්වන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ca49d66716d8..6019e1897500 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačidlo <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Domov"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Späť"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Šípka nahor"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Šípka nadol"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Šípka doľava"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Šípka doprava"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do stredu"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Medzerník"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Upozornenia"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Klávesové skratky"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Prepnúť rozloženie klávesnice"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"alebo"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Vymazať vyhľadávací dopyt"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Skratky"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Hľadajte skratky"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nenašli sa žiadne skratky"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Vstup"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Otvorenie apl."</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuálna aplik."</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Zobrazujú sa výsledky vyhľadávania"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Zobrazujú sa skratky systému"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Zobrazujú sa skratky vstupu"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Zobrazujú sa skratky na otvorenie aplikácií"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Zobrazujú sa skratky aktuálnej aplikácie"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Zobrazenie upozornení"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Vytvorenie snímky obrazovky"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Zobrazenie skratiek"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index c779b65c239f..80ce4ec4d08e 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butoni <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Kreu"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Prapa"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Shigjeta lart"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Shigjeta poshtë"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Shigjeta majtas"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Shigjeta djathtas"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Qendror"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Hapësirë"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Njoftimet"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Shkurtoret e tastierës"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Ndërro strukturën e tastierës"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"ose"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Pastro pyetjen e kërkimit"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Shkurtoret"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Kërko shkurtoret"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nuk u gjet shkurtore"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Hyrja"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Apl. e hapura"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Apl. aktual"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Po shfaqen rezultatet e kërkimit"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Po shfaqen shkurtoret e sistemit"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Po shfaqen shkurtoret e hyrjes"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Po shfaqen shkurtoret që hapin aplikacionet"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Po shfaqen shkurtoret për aplikacionin aktual"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Shiko njoftimet"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Nxirr një pamje ekrani"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Shfaq shkurtoret"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 48673b90ca8f..a6d00ba088a5 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Дугме <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Тастер Почетна"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Тастер Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелица нагоре"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелица надоле"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелица налево"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелица надесно"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Тастер са централном стрелицом"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Размак"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Обавештења"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Тастерске пречице"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Промени распоред тастатуре"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Обриши упит за претрагу"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Пречице"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Претражите пречице"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Нису пронађене пречице"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Унос"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Отварање аплик"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Актуелна аплик"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Приказују се резултати претраге"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Приказују се системске пречице"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Приказују се пречице за уметање"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Приказују се пречице за отварање апликација"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Приказују се пречице за актуелну апликацију"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Прикажи обавештења"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Направи снимак екрана"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Прикажи пречице"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 0b2cab0454f8..6b6fa6ae7290 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knappen <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Start"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Tillbaka"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Uppåtpil"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedåtpil"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vänsterpil"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Högerpil"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrera"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Blanksteg"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Aviseringar"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Kortkommandon"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Byt tangentbordslayout"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"eller"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Rensa sökfråga"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kortkommandon"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sök efter kortkommando"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Inga resultat"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Inmatning"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Öppna appar"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Aktuell app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Visar sökresultat"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Visar genvägar för systemet"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Visar genvägar för indata"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Visar genvägar som öppnar appar"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Visar genvägar för den aktuella appen"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Se aviseringar"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ta skärmbild"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Se genvägar"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 7989a3539cf5..f247da5a6579 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> பட்டன்"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"ஹோம்"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"பேக்"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"மேல்நோக்கிய அம்புக்குறி"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"கீழ்நோக்கிய அம்புக்குறி"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"இடது அம்புக்குறி"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"வலது அம்புக்குறி"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"நடு"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"ஸ்பேஸ்"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"அறிவிப்புகள்"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"கீபோர்டு ஷார்ட்கட்கள்"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"கீபோர்டு லே அவுட்டை மாற்று"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"அல்லது"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"தேடல் வினவலை அழிக்கும்"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"ஷார்ட்கட்கள்"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ஷார்ட்கட்களைத் தேடுக"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ஷார்ட்கட்கள் எதுவுமில்லை"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"உள்ளீடு"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"திறந்த ஆப்ஸ்"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"தற்போதைய ஆப்ஸ்"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"தேடல் முடிவுகளைக் காட்டுகிறது"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"சிஸ்டம் ஷார்ட்கட்களைக் காட்டுகிறது"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"உள்ளிடுவதற்கான ஷார்ட்கட்களைக் காட்டுகிறது"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ஆப்ஸைத் திறப்பதற்கான ஷார்ட்கட்களைக் காட்டுகிறது"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"தற்போது உபயோகத்தில் இருக்கும் ஆப்ஸுக்கான ஷார்ட்கட்களைக் காட்டுகிறது"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"அறிவிப்புகளைக் காட்டுதல்"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ஸ்கிரீன்ஷாட் எடுத்தல்"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"ஷார்ட்கட்களைக் காட்டுதல்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 6b2c032fcb22..cdc57c8fcc24 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"బటన్ <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"వెనుకకు"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"పై వైపు బాణం"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"కింది వైపు బాణం"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ఎడమ వైపు బాణం"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"కుడి వైపు బాణం"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"మధ్య"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"అంతరం"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"నోటిఫికేషన్లు"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"కీబోర్డ్ షార్ట్కట్లు"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"కీబోర్డ్ లేఅవుట్ను మార్చండి"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"లేదా"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"సెర్చ్ క్వెరీని క్లియర్ చేయండి"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"షార్ట్కట్లు"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"షార్ట్కట్స్ సెర్చ్ చేయండి"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"షార్ట్కట్లు ఏవీ లేవు"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ఇన్పుట్"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"యాప్స్ తెరవండి"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"ప్రస్తుత యాప్"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"సెర్చ్ ఫలితాలను చూపుతోంది"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"సిస్టమ్ షార్ట్కట్లను చూపుతోంది"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ఇన్పుట్ షార్ట్కట్లను చూపుతోంది"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"యాప్లను తెరిచే షార్ట్కట్లను చూపుతోంది"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"ప్రస్తుత యాప్ కోసం షార్ట్కట్లను చూపుతోంది"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"నోటిఫికేషన్లను చూడండి"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"స్క్రీన్షాట్ను తీయండి"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"షార్ట్కట్లను చూపించండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 3c5396fe4823..945563bd3b1a 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"กลับ"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ลูกศรขึ้น"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ลูกศรลง"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ลูกศรซ้าย"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ลูกศรขวา"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"กึ่งกลาง"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"วรรค"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"การแจ้งเตือน"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"แป้นพิมพ์ลัด"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"สลับรูปแบบแป้นพิมพ์"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"หรือ"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ล้างคำค้นหา"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"แป้นพิมพ์ลัด"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ค้นหาแป้นพิมพ์ลัด"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ไม่พบแป้นพิมพ์ลัด"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"อินพุต"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"แอปที่เปิดอยู่"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"แอปปัจจุบัน"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"แสดงผลการค้นหา"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"แสดงแป้นพิมพ์ลัดสำหรับระบบ"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"แสดงแป้นพิมพ์ลัดสำหรับอินพุต"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"แสดงแป้นพิมพ์ลัดที่เปิดแอป"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"แสดงแป้นพิมพ์ลัดสำหรับแอปปัจจุบัน"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"ดูการแจ้งเตือน"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"ถ่ายภาพหน้าจอ"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"แสดงแป้นพิมพ์ลัด"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 21111a405308..c251ae888fc9 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button na <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pataas na arrow"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pababang arrow"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pakaliwang arrow"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pakanang arrow"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Mga Notification"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Mga Keyboard Shortcut"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Magpalit ng layout ng keyboard"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"o"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"I-clear ang query sa paghahanap"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Mga Shortcut"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Maghanap ng mga shortcut"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Walang nakitang shortcut"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Input"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Buksan ang app"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Kasalukuyang app"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Ipinapakita ang mga resulta ng paghahanap"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Ipinapakita ang mga shortcut ng system"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Ipinapakita ang mga shortcut ng input"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Ipinapakita ang mga shortcut na nagbubukas ng mga app"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Ipinapakita ang mga shortcut para sa kasalukuyang app"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Tumingin ng mga notification"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Kumuha ng screenshot"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Ipakita ang mga shortcut"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 08b0251d6438..f56b331a76f4 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> düğmesi"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yukarı ok"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ok"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ok"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ok"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Orta"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluk"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Bildirimler"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Klavye Kısayolları"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klavye düzenini değiştir"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"veya"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Arama sorgusunu temizle"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Kısayollar"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Kısayol araması yapın"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Kısayol bulunamadı"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Giriş"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Uygulamaları açma"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Mevcut uygulama"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Arama sonuçları gösteriliyor"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Sistem kısayolları gösteriliyor"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Giriş kısayolları gösteriliyor"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Uygulamaları açan kısayollar gösteriliyor"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Mevcut uygulamanın kısayolları gösteriliyor"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Bildirimleri görüntüle"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ekran görüntüsü al"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Kısayolları göster"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 9048b6326368..2ea23263a217 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрілка вгору"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрілка вниз"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрілка вліво"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрілка вправо"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центр"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Пробіл"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Сповіщення"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Комбінації клавіш"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Змінити розкладку клавіатури"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"або"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Очистити пошуковий запит"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Швидкі команди"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Пошук швидких команд"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Швидк. команд не знайдено"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Метод введення"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Відкр. додатки"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Поточн. додаток"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Показано результати пошуку"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Показано комбінації клавіш для системних дій"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Показано комбінації клавіш для введення даних"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Показано комбінації клавіш, які відкривають додатки"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Показано комбінації клавіш для поточного додатка"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Переглянути сповіщення"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Зробити знімок екрана"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Показати комбінації клавіш"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index d9bb0aeb0abb..6fb08c42152b 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"بٹن <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"پیچھے"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"اوپر تیر کا نشان"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"نیچے تیر کا نشان"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"بایاں تیر"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"دایاں تیر"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"سینٹر"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"اطلاعات"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"کی بورڈ شارٹ کٹس"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"کی بورڈ لے آؤٹ سوئچ کریں"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"یا"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"تلاش کا استفسار صاف کریں"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"شارٹ کٹس"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"شارٹ کٹس تلاش کریں"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"کوئی شارٹ کٹ نہیں ملا"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"ان پٹ"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"ایپس کھولیں"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"موجودہ ایپ"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"تلاش کے نتائج دکھائے جا رہے ہیں"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"سسٹم کے شارٹ کٹس دکھائے جا رہے ہیں"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"ان پٹ شارٹ کٹس دکھائے جا رہے ہیں"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"ایپس کو کھولنے والے شارٹ کٹس دکھائے جا رہے ہیں"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"موجودہ ایپ کے شارٹ کٹس دکھائے جا رہے ہیں"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"اطلاعات دیکھیں"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"اسکرین شاٹ لیں"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"شارٹ کٹس دکھائيں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 5696c61fba35..4f1e1a1c6d39 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> tugmasi"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Bosh ekran"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Orqaga"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Tepaga strelka"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pastga strelka"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Chapga strelka"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oʻngga strelka"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Markaziy ko‘rsatkichli chiziq"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Probel"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Bildirishnomalar"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Tezkor tugmalar"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Klaviatura terilmasini almashtirish"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"yoki"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Qidiruv soʻrovini tozalash"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Tezkor tugmalar"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tezkor tugmalar qidiruvi"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Tezkor tugmalar topilmadi"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Kiritish"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ochiq ilovalar"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Joriy ilova"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Qidiruv natijalarini koʻrsatadi"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Tizim tezkor tugmalari koʻrsatiladi"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Kirish bilan bogʻliq tezkor tugmalarni koʻrsatiladi"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Ilovalarni ochuvchi tezkor tugmalar koʻrsatiladi"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Joriy ilova uchun tezkor tugmalar koʻrsatiladi"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Bildirishnomalarni ochish"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Skrinshot olish"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Yorliqlarni ochish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index c281d906b749..aae84c2c6e5c 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nút <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Quay lại"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Mũi tên lên"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Mũi tên xuống"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Mũi tên trái"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Mũi tên phải"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Giữa"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Dấu cách"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Thông báo"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Phím tắt"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Chuyển đổi bố cục bàn phím"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"hoặc"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Xoá cụm từ tìm kiếm"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Lối tắt"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Lối tắt tìm kiếm"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Không tìm thấy lối tắt"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Đầu vào"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Ứng dụng đang mở"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"Ứng dụng hiện tại"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Hiện các kết quả tìm kiếm"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Hiện các phím tắt cho hệ thống"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Hiện các phím tắt cho phương thức nhập"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Hiện các phím tắt mở ứng dụng"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Hiện các phím tắt cho ứng dụng hiện tại"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Xem thông báo"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Chụp ảnh màn hình"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Hiện phím tắt"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 7cde292938e8..6e73dc1df4df 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上键"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下键"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左键"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右键"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中心"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"空格"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"通知"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"键盘快捷键"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切换键盘布局"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜索查询"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快捷键"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜索快捷键"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"未找到任何快捷键"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"输入"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已开应用"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"当前应用"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"目前显示的是搜索结果"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"目前显示的是系统快捷键"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"目前显示的是输入法的快捷键"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"目前显示的是用于打开应用的快捷键"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"目前显示的是当前应用的快捷键"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"查看通知"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"截取屏幕截图"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"显示快捷键"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index a997d060ee38..1286fce4425f 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 鍵"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭咀"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭咀"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭咀"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭咀"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"箭咀中央"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"空格"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"通知"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"鍵盤快速鍵"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切換鍵盤配置"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜尋查詢"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快速鍵"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜尋快速鍵"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"找不到快速鍵"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"輸入"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已開應用程式"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"目前的應用程式"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"顯示緊搜尋結果"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"顯示緊系統嘅快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"顯示緊輸入裝置嘅快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"顯示緊打開應用程式嘅快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"顯示緊而家呢個應用程式用到嘅快速鍵"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"查看通知"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"擷取螢幕截圖"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"顯示快速鍵"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 9900adf22da5..ff9b88a4e004 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Home 鍵"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭頭"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭頭"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭頭"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭頭"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央鍵"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"空格鍵"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"通知"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"鍵盤快速鍵"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"切換鍵盤配置"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"或"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"清除搜尋查詢"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"快速鍵"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"搜尋快速鍵"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"找不到快速鍵"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"輸入"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已開啟的應用程式"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"目前的應用程式"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"顯示搜尋結果"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"顯示系統快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"顯示輸入快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"顯示可開啟應用程式的快速鍵"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"顯示目前應用程式的快速鍵"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"查看通知"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"拍攝螢幕截圖"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"顯示快速鍵"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 42ddb35aa16a..6c4261a9b035 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -640,14 +640,10 @@ <string name="keyboard_key_button_template" msgid="8005673627272051429">"Inkinobho <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Ekhaya"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Emuva"</string> - <!-- no translation found for keyboard_key_dpad_up (7199805608386368673) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_down (3354221123220737397) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_left (144176368026538621) --> - <skip /> - <!-- no translation found for keyboard_key_dpad_right (8485763312139820037) --> - <skip /> + <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Umcibisholo waphezulu"</string> + <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Umcibisholo waphansi"</string> + <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Umcibisholo wangokwesokunxele"</string> + <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Umcibisholo wangokwesokudla"</string> <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Maphakathi"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> <string name="keyboard_key_space" msgid="6980847564173394012">"Isikhala"</string> @@ -675,10 +671,8 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Izaziso"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Izinqamulelo Zekhibhodi"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Shintsha isakhiwo sekhibhodi"</string> - <!-- no translation found for keyboard_shortcut_join (3578314570034512676) --> - <skip /> - <!-- no translation found for keyboard_shortcut_clear_text (6631051796030377857) --> - <skip /> + <string name="keyboard_shortcut_join" msgid="3578314570034512676">"noma"</string> + <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Sula umbuzo wosesho"</string> <string name="keyboard_shortcut_search_list_title" msgid="1156178106617830429">"Izinqamuleli"</string> <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Sesha izinqamuleli"</string> <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Azikho izinqamuleli ezitholakele"</string> @@ -686,16 +680,11 @@ <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Okokufaka"</string> <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"Vula ama-app"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"I-app yamanje"</string> - <!-- no translation found for keyboard_shortcut_a11y_show_search_results (2865241062981833705) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_system (7744143131119370483) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_input (4589316004510335529) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_open_apps (6175417687221004059) --> - <skip /> - <!-- no translation found for keyboard_shortcut_a11y_filter_current_app (7944592357493737911) --> - <skip /> + <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"Ibonisa imiphumela yosesho"</string> + <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"Ibonisa izinqamuleli zesistimu"</string> + <string name="keyboard_shortcut_a11y_filter_input" msgid="4589316004510335529">"Ibonisa izinqamuleli zokufakwayo"</string> + <string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Ibonisa izinqamuleli ezivula ama-app"</string> + <string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Ibonisa izinqamuleli ze-app yamanje"</string> <string name="group_system_access_notification_shade" msgid="1619028907006553677">"Buka izaziso"</string> <string name="group_system_full_screenshot" msgid="5742204844232667785">"Thatha isithombe-skrini"</string> <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Bonisa izinqamuleli"</string> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 1838795a57d6..cf63cc74521d 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -223,6 +223,8 @@ <item type="id" name="lock_icon_bg" /> <item type="id" name="burn_in_layer" /> <item type="id" name="communal_tutorial_indicator" /> + <item type="id" name="nssl_placeholder_barrier_bottom" /> + <item type="id" name="ambient_indication_container" /> <!-- Privacy dialog --> <item type="id" name="privacy_dialog_close_app_button" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7ca0b6ee8d9f..78b701caa3b6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -824,6 +824,13 @@ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="quick_settings_screen_record_stop">Stop</string> + <!-- QuickSettings: Record Issue tile [CHAR LIMIT=NONE] --> + <string name="qs_record_issue_label">Record Issue</string> + <!-- QuickSettings: Text to prompt the user to begin a new recording [CHAR LIMIT=20] --> + <string name="qs_record_issue_start">Start</string> + <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> + <string name="qs_record_issue_stop">Stop</string> + <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_onehanded_label">One-handed mode</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index f1a40077f3f9..e27a328a847a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -119,9 +119,8 @@ data class UnreleasedFlag constructor( data class ReleasedFlag constructor( override val name: String, override val namespace: String, - override val teamfood: Boolean = false, override val overridden: Boolean = false -) : BooleanFlag(name, namespace, true, teamfood, overridden) +) : BooleanFlag(name, namespace, true, teamfood = false, overridden) /** * A Flag that reads its default values from a resource overlay instead of code. @@ -132,8 +131,9 @@ data class ResourceBooleanFlag constructor( override val name: String, override val namespace: String, @BoolRes override val resourceId: Int, +) : ResourceFlag<Boolean> { override val teamfood: Boolean = false -) : ResourceFlag<Boolean> +} /** * A Flag that can reads its overrides from System Properties. @@ -147,7 +147,6 @@ data class SysPropBooleanFlag constructor( override val namespace: String, override val default: Boolean = false, ) : SysPropFlag<Boolean> { - // TODO(b/268520433): Teamfood not supported for sysprop flags yet. override val teamfood: Boolean = false } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index c505bd502985..df7182b90f12 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -21,6 +21,7 @@ import android.os.Build; import android.text.TextUtils; import android.view.View; +import com.android.internal.jank.Cuj; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.jank.InteractionJankMonitor.Configuration; @@ -29,40 +30,25 @@ import java.lang.annotation.RetentionPolicy; public final class InteractionJankMonitorWrapper { // Launcher journeys. - public static final int CUJ_APP_LAUNCH_FROM_RECENTS = - InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS; - public static final int CUJ_APP_LAUNCH_FROM_ICON = - InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON; - public static final int CUJ_APP_CLOSE_TO_HOME = - InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; + public static final int CUJ_APP_LAUNCH_FROM_RECENTS = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS; + public static final int CUJ_APP_LAUNCH_FROM_ICON = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON; + public static final int CUJ_APP_CLOSE_TO_HOME = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME; public static final int CUJ_APP_CLOSE_TO_HOME_FALLBACK = - InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; - public static final int CUJ_APP_CLOSE_TO_PIP = - InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP; - public static final int CUJ_QUICK_SWITCH = - InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH; - public static final int CUJ_OPEN_ALL_APPS = - InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS; - public static final int CUJ_CLOSE_ALL_APPS_SWIPE = - InteractionJankMonitor.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE; - public static final int CUJ_CLOSE_ALL_APPS_TO_HOME = - InteractionJankMonitor.CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME; - public static final int CUJ_ALL_APPS_SCROLL = - InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL; - public static final int CUJ_APP_LAUNCH_FROM_WIDGET = - InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET; - public static final int CUJ_SPLIT_SCREEN_ENTER = - InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER; + Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; + public static final int CUJ_APP_CLOSE_TO_PIP = Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP; + public static final int CUJ_QUICK_SWITCH = Cuj.CUJ_LAUNCHER_QUICK_SWITCH; + public static final int CUJ_OPEN_ALL_APPS = Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS; + public static final int CUJ_CLOSE_ALL_APPS_SWIPE = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE; + public static final int CUJ_CLOSE_ALL_APPS_TO_HOME = Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME; + public static final int CUJ_ALL_APPS_SCROLL = Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL; + public static final int CUJ_APP_LAUNCH_FROM_WIDGET = Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET; + public static final int CUJ_SPLIT_SCREEN_ENTER = Cuj.CUJ_SPLIT_SCREEN_ENTER; public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = - InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; - public static final int CUJ_RECENTS_SCROLLING = - InteractionJankMonitor.CUJ_RECENTS_SCROLLING; - public static final int CUJ_APP_SWIPE_TO_RECENTS = - InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS; - public static final int CUJ_OPEN_SEARCH_RESULT = - InteractionJankMonitor.CUJ_LAUNCHER_OPEN_SEARCH_RESULT; - public static final int CUJ_LAUNCHER_UNFOLD_ANIM = - InteractionJankMonitor.CUJ_LAUNCHER_UNFOLD_ANIM; + Cuj.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; + public static final int CUJ_RECENTS_SCROLLING = Cuj.CUJ_RECENTS_SCROLLING; + public static final int CUJ_APP_SWIPE_TO_RECENTS = Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS; + public static final int CUJ_OPEN_SEARCH_RESULT = Cuj.CUJ_LAUNCHER_OPEN_SEARCH_RESULT; + public static final int CUJ_LAUNCHER_UNFOLD_ANIM = Cuj.CUJ_LAUNCHER_UNFOLD_ANIM; @IntDef({ CUJ_APP_LAUNCH_FROM_RECENTS, @@ -89,7 +75,7 @@ public final class InteractionJankMonitorWrapper { * Begin a trace session. * * @param v an attached view. - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. */ public static void begin(View v, @CujType int cujType) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; @@ -100,7 +86,7 @@ public final class InteractionJankMonitorWrapper { * Begin a trace session. * * @param v an attached view. - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. * @param timeout duration to cancel the instrumentation in ms */ public static void begin(View v, @CujType int cujType, long timeout) { @@ -115,7 +101,7 @@ public final class InteractionJankMonitorWrapper { * Begin a trace session. * * @param v an attached view. - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. * @param tag the tag to distinguish different flow of same type CUJ. */ public static void begin(View v, @CujType int cujType, String tag) { @@ -131,7 +117,7 @@ public final class InteractionJankMonitorWrapper { /** * End a trace session. * - * @param cujType the specific {@link InteractionJankMonitor.CujType}. + * @param cujType the specific {@link Cuj.CujType}. */ public static void end(@CujType int cujType) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return; diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt index aef83710c17b..f9fe67ac2076 100644 --- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt +++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt @@ -42,7 +42,7 @@ object FlagsFactory { name: String, namespace: String = "systemui", ): ReleasedFlag { - val flag = ReleasedFlag(name = name, namespace = namespace, teamfood = false) + val flag = ReleasedFlag(name = name, namespace = namespace) checkForDupesAndAdd(flag) return flag } @@ -57,7 +57,6 @@ object FlagsFactory { name = name, namespace = namespace, resourceId = resourceId, - teamfood = false, ) checkForDupesAndAdd(flag) return flag diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt index f4b429659d8a..aedf0ce21c24 100644 --- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt +++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt @@ -42,7 +42,7 @@ object FlagsFactory { name: String, namespace: String = "systemui", ): ReleasedFlag { - val flag = ReleasedFlag(name = name, namespace = namespace, teamfood = false) + val flag = ReleasedFlag(name = name, namespace = namespace) flagMap[name] = flag return flag } @@ -57,7 +57,6 @@ object FlagsFactory { name = name, namespace = namespace, resourceId = resourceId, - teamfood = false, ) flagMap[name] = flag return flag diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index be2c65fa4a45..cdd7b804fdf8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -72,7 +72,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.ui.SystemBarUtilsState; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.SecureSettings; @@ -105,7 +105,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel; private final KeyguardRootViewModel mKeyguardRootViewModel; private final ConfigurationState mConfigurationState; - private final ConfigurationController mConfigurationController; + private final SystemBarUtilsState mSystemBarUtilsState; private final DozeParameters mDozeParameters; private final ScreenOffAnimationController mScreenOffAnimationController; private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore; @@ -183,7 +183,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS KeyguardSliceViewController keyguardSliceViewController, NotificationIconAreaController notificationIconAreaController, LockscreenSmartspaceController smartspaceController, - ConfigurationController configurationController, + SystemBarUtilsState systemBarUtilsState, ScreenOffAnimationController screenOffAnimationController, StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker, KeyguardUnlockAnimationController keyguardUnlockAnimationController, @@ -208,7 +208,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardSliceViewController = keyguardSliceViewController; mNotificationIconAreaController = notificationIconAreaController; mSmartspaceController = smartspaceController; - mConfigurationController = configurationController; + mSystemBarUtilsState = systemBarUtilsState; mScreenOffAnimationController = screenOffAnimationController; mIconViewBindingFailureTracker = iconViewBindingFailureTracker; mSecureSettings = secureSettings; @@ -619,13 +619,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mAodIconsBindHandle.dispose(); } if (nic != null) { - final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind( - nic, - mAodIconsViewModel, - mConfigurationState, - mConfigurationController, - mIconViewBindingFailureTracker, - mAodIconViewStore); + final DisposableHandle viewHandle = + NotificationIconContainerViewBinder.bindWhileAttached( + nic, + mAodIconsViewModel, + mConfigurationState, + mSystemBarUtilsState, + mIconViewBindingFailureTracker, + mAodIconViewStore); final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility( nic, mKeyguardRootViewModel.isNotifIconContainerVisible(), diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java index 38a8cd39a078..c4aa7a26be18 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -104,4 +104,14 @@ public interface KeyguardSecurityCallback { */ default void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { } + + /** + * Shows the security screen that should be shown. + * + * This can be considered as a "refresh" of the bouncer view. Based on certain parameters, + * we might switch to a different bouncer screen. e.g. SimPin to SimPuk. + */ + default void showCurrentSecurityScreen() { + + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index f706301df1ca..0a4378e07b45 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -27,6 +27,8 @@ import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY; import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER; import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE; +import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPin; +import static com.android.keyguard.KeyguardSecurityModel.SecurityMode.SimPuk; import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE; import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES; @@ -99,6 +101,7 @@ import com.android.systemui.util.settings.GlobalSettings; import dagger.Lazy; import java.io.File; +import java.util.Arrays; import java.util.Optional; import javax.inject.Inject; @@ -164,8 +167,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } mCurrentUser = mSelectedUserInteractor.getSelectedUserId(); showPrimarySecurityScreen(false); - if (mCurrentSecurityMode != SecurityMode.SimPin - && mCurrentSecurityMode != SecurityMode.SimPuk) { + if (mCurrentSecurityMode != SimPin + && mCurrentSecurityMode != SimPuk) { reinflateViewFlipper((l) -> { }); } @@ -334,6 +337,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { mViewMediatorCallback.setNeedsInput(needsInput); } + + @Override + public void showCurrentSecurityScreen() { + showPrimarySecurityScreen(false); + } }; private final SwipeListener mSwipeListener = new SwipeListener() { @@ -888,7 +896,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard finish = true; eventSubtype = BOUNCER_DISMISS_SIM; uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; - } else { + } else if (Arrays.asList(SimPin, SimPuk).contains(securityMode)) { + // There are additional screens to the sim pin/puk flow. showSecurityScreen(securityMode); } break; @@ -1095,8 +1104,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private void configureMode() { - boolean useSimSecurity = mCurrentSecurityMode == SecurityMode.SimPin - || mCurrentSecurityMode == SecurityMode.SimPuk; + boolean useSimSecurity = mCurrentSecurityMode == SimPin + || mCurrentSecurityMode == SimPuk; int mode = KeyguardSecurityContainer.MODE_DEFAULT; if (canDisplayUserSwitcher() && !useSimSecurity) { mode = KeyguardSecurityContainer.MODE_USER_SWITCHER; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 6e242084d68c..c5e70703cd2b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; import android.annotation.NonNull; @@ -60,7 +62,7 @@ public class KeyguardSimPinViewController // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would // be displayed to inform user about the number of remaining PIN attempts left. private boolean mShowDefaultMessage; - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private int mSubId = INVALID_SUBSCRIPTION_ID; private AlertDialog mRemainingAttemptsDialog; private ImageView mSimImageView; @@ -68,6 +70,12 @@ public class KeyguardSimPinViewController @Override public void onSimStateChanged(int subId, int slotId, int simState) { if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + // If subId has gone to PUK required then we need to go to the PUK screen. + if (subId == mSubId && simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) { + getKeyguardSecurityCallback().showCurrentSecurityScreen(); + return; + } + if (simState == TelephonyManager.SIM_STATE_READY) { mRemainingAttempts = -1; resetState(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 37bd9b287ebd..9c61a8a3cd8b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1277,6 +1277,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final FaceAuthenticationListener mFaceAuthenticationListener = new FaceAuthenticationListener() { + public void onAuthenticatedChanged(boolean isAuthenticated) { + if (!isAuthenticated) { + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onFacesCleared(); + } + } + } + } + @Override public void onAuthEnrollmentStateChanged(boolean enrolled) { notifyAboutEnrollmentChange(TYPE_FACE); @@ -1961,7 +1972,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab protected void handleStartedGoingToSleep(int arg1) { Assert.isMainThread(); - clearBiometricRecognized(); + clearFingerprintRecognized(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3010,7 +3021,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab void handleUserSwitching(int userId, Runnable resultCallback) { mLogger.logUserSwitching(userId, "from UserTracker"); Assert.isMainThread(); - clearBiometricRecognized(); + clearFingerprintRecognized(); boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId); mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId), trustUsuallyManaged, "userSwitching"); @@ -3560,25 +3571,30 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mServiceStates.get(subId); } - public void clearBiometricRecognized() { - clearBiometricRecognized(UserHandle.USER_NULL); + /** + * Resets the fingerprint authenticated state to false. + */ + public void clearFingerprintRecognized() { + clearFingerprintRecognized(UserHandle.USER_NULL); } - public void clearBiometricRecognizedWhenKeyguardDone(int unlockedUser) { - clearBiometricRecognized(unlockedUser); + /** + * Resets the fingerprint authenticated state to false. + */ + public void clearFingerprintRecognizedWhenKeyguardDone(int unlockedUser) { + clearFingerprintRecognized(unlockedUser); } - private void clearBiometricRecognized(int unlockedUser) { + private void clearFingerprintRecognized(int unlockedUser) { Assert.isMainThread(); mUserFingerprintAuthenticated.clear(); mTrustManager.clearAllBiometricRecognized(FINGERPRINT, unlockedUser); - mTrustManager.clearAllBiometricRecognized(FACE, unlockedUser); - mLogger.d("clearBiometricRecognized"); + mLogger.d("clearFingerprintRecognized"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onBiometricsCleared(); + cb.onFingerprintsCleared(); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 02dd3312c587..9d216dcede2b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -291,9 +291,14 @@ public class KeyguardUpdateMonitorCallback { public void onLogoutEnabledChanged() { } /** - * Called when authenticated biometrics are cleared. + * Called when authenticated fingerprint biometrics are cleared. */ - public void onBiometricsCleared() { } + public void onFingerprintsCleared() { } + + /** + * Called when authenticated face biometrics have cleared. + */ + public void onFacesCleared() { } /** * Called when the secondary lock screen requirement changes. diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index bf445177f3f8..c3f64803758b 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -110,6 +110,10 @@ public class SystemUIApplication extends Application implements View.setTracedRequestLayoutClassClass( SystemProperties.get("persist.debug.trace_request_layout_class", null)); + if (Flags.enableLayoutTracing()) { + View.setTraceLayoutSteps(true); + } + if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index c03e403b754b..a98990af00c7 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -59,8 +59,8 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.res.R; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; +import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; import java.lang.annotation.Retention; @@ -671,17 +671,17 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } private Rect getDraggableWindowBounds() { - final int layoutMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.magnification_switch_button_margin); final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); final Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + // re-measure the settings panel view so that we can get the correct view size to inset + int unspecificSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + mSettingView.measure(unspecificSpec, unspecificSpec); + final Rect boundRect = new Rect(windowMetrics.getBounds()); boundRect.offsetTo(0, 0); - boundRect.inset(0, 0, mParams.width, mParams.height); + boundRect.inset(0, 0, mSettingView.getMeasuredWidth(), mSettingView.getMeasuredHeight()); boundRect.inset(windowInsets); - boundRect.inset(layoutMargin, layoutMargin); - return boundRect; } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index dd4ca92fcc02..fda23b7f2a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -24,9 +24,9 @@ import android.os.UserHandle import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationResultModel -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -60,14 +60,6 @@ import kotlinx.coroutines.withContext /** Defines interface for classes that can access authentication-related application state. */ interface AuthenticationRepository { /** - * Whether the auto confirm feature is enabled for the currently-selected user. - * - * Note that the length of the PIN is also important to take into consideration, please see - * [hintedPinLength]. - */ - val isAutoConfirmFeatureEnabled: StateFlow<Boolean> - - /** * Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user * in order to unlock the device. */ @@ -88,10 +80,22 @@ interface AuthenticationRepository { val isPatternVisible: StateFlow<Boolean> /** - * The current authentication throttling state, set when the user has to wait before being able - * to try another authentication attempt. `null` indicates throttling isn't active. + * The current authentication lockout (aka "throttling") state, set when the user has to wait + * before being able to try another authentication attempt. `null` indicates throttling isn't + * active. */ - val throttling: MutableStateFlow<AuthenticationThrottlingModel?> + val lockout: MutableStateFlow<AuthenticationLockoutModel?> + + /** Whether throttling has occurred at least once since the last successful authentication. */ + val hasLockoutOccurred: MutableStateFlow<Boolean> + + /** + * Whether the auto confirm feature is enabled for the currently-selected user. + * + * Note that the length of the PIN is also important to take into consideration, please see + * [hintedPinLength]. + */ + val isAutoConfirmFeatureEnabled: StateFlow<Boolean> /** * The currently-configured authentication method. This determines how the authentication @@ -135,22 +139,25 @@ interface AuthenticationRepository { /** Reports an authentication attempt. */ suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) + /** Reports that the user has entered a temporary device lockout (throttling). */ + suspend fun reportLockoutStarted(durationMs: Int) + /** Returns the current number of failed authentication attempts. */ suspend fun getFailedAuthenticationAttemptCount(): Int /** - * Returns the timestamp for when the current throttling will end, allowing the user to attempt + * Returns the timestamp for when the current lockout will end, allowing the user to attempt * authentication again. * * Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime]. */ - suspend fun getThrottlingEndTimestamp(): Long + suspend fun getLockoutEndTimestamp(): Long /** - * Sets the throttling timeout duration (time during which the user should not be allowed to + * Sets the lockout timeout duration (time during which the user should not be allowed to * attempt authentication). */ - suspend fun setThrottleDuration(durationMs: Int) + suspend fun setLockoutDuration(durationMs: Int) /** * Checks the given [LockscreenCredential] to see if it's correct, returning an @@ -172,11 +179,6 @@ constructor( mobileConnectionsRepository: MobileConnectionsRepository, ) : AuthenticationRepository { - override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> = - refreshingFlow( - initialValue = false, - getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled, - ) override val authenticationChallengeResult = MutableSharedFlow<Boolean>() override val hintedPinLength: Int = 6 @@ -187,11 +189,15 @@ constructor( getFreshValue = lockPatternUtils::isVisiblePatternEnabled, ) - override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> = - MutableStateFlow(null) + override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null) - private val selectedUserId: Int - get() = userRepository.getSelectedUserInfo().id + override val hasLockoutOccurred: MutableStateFlow<Boolean> = MutableStateFlow(false) + + override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> = + refreshingFlow( + initialValue = false, + getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled, + ) override val authenticationMethod: Flow<AuthenticationMethodModel> = combine(userRepository.selectedUserInfo, mobileConnectionsRepository.isAnySimSecure) { @@ -249,19 +255,25 @@ constructor( } } + override suspend fun reportLockoutStarted(durationMs: Int) { + return withContext(backgroundDispatcher) { + lockPatternUtils.reportPasswordLockout(durationMs, selectedUserId) + } + } + override suspend fun getFailedAuthenticationAttemptCount(): Int { return withContext(backgroundDispatcher) { lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId) } } - override suspend fun getThrottlingEndTimestamp(): Long { + override suspend fun getLockoutEndTimestamp(): Long { return withContext(backgroundDispatcher) { lockPatternUtils.getLockoutAttemptDeadline(selectedUserId) } } - override suspend fun setThrottleDuration(durationMs: Int) { + override suspend fun setLockoutDuration(durationMs: Int) { withContext(backgroundDispatcher) { lockPatternUtils.setLockoutAttemptDeadline(selectedUserId, durationMs) } @@ -273,13 +285,16 @@ constructor( return withContext(backgroundDispatcher) { try { val matched = lockPatternUtils.checkCredential(credential, selectedUserId) {} - AuthenticationResultModel(isSuccessful = matched, throttleDurationMs = 0) + AuthenticationResultModel(isSuccessful = matched, lockoutDurationMs = 0) } catch (ex: LockPatternUtils.RequestThrottledException) { - AuthenticationResultModel(isSuccessful = false, throttleDurationMs = ex.timeoutMs) + AuthenticationResultModel(isSuccessful = false, lockoutDurationMs = ex.timeoutMs) } } } + private val selectedUserId: Int + get() = userRepository.getSelectedUserInfo().id + /** * Returns a [StateFlow] that's automatically kept fresh. The passed-in [getFreshValue] is * invoked on a background thread every time the selected user is changed and every time a new diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index eb87505df6a5..797154e85082 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -20,15 +20,16 @@ import com.android.app.tracing.TraceUtils.Companion.withContext import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential import com.android.systemui.authentication.data.repository.AuthenticationRepository +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.time.SystemClock import javax.inject.Inject +import kotlin.math.ceil import kotlin.math.max import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher @@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext /** * Hosts application business logic related to user authentication. @@ -85,25 +85,25 @@ constructor( val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod /** - * The current authentication throttling state, set when the user has to wait before being able - * to try another authentication attempt. `null` indicates throttling isn't active. + * The current authentication lockout (aka "throttling") state, set when the user has to wait + * before being able to try another authentication attempt. `null` indicates lockout isn't + * active. */ - val throttling: StateFlow<AuthenticationThrottlingModel?> = repository.throttling + val lockout: StateFlow<AuthenticationLockoutModel?> = repository.lockout /** * Whether the auto confirm feature is enabled for the currently-selected user. * * Note that the length of the PIN is also important to take into consideration, please see * [hintedPinLength]. - * - * During throttling, this is always disabled (`false`). */ val isAutoConfirmEnabled: StateFlow<Boolean> = - combine(repository.isAutoConfirmFeatureEnabled, repository.throttling) { + combine(repository.isAutoConfirmFeatureEnabled, repository.hasLockoutOccurred) { featureEnabled, - throttling -> - // Disable auto-confirm during throttling. - featureEnabled && throttling == null + hasLockoutOccurred -> + // Disable auto-confirm if lockout occurred since the last successful + // authentication attempt. + featureEnabled && !hasLockoutOccurred } .stateIn( scope = applicationScope, @@ -140,7 +140,7 @@ constructor( /** Whether the "enhanced PIN privacy" setting is enabled for the current user. */ val isPinEnhancedPrivacyEnabled: StateFlow<Boolean> = repository.isPinEnhancedPrivacyEnabled - private var throttlingCountdownJob: Job? = null + private var lockoutCountdownJob: Job? = null init { applicationScope.launch { @@ -189,8 +189,8 @@ constructor( val authMethod = getAuthenticationMethod() val skipCheck = when { - // Throttling is active, the UI layer should not have called this; skip the attempt. - throttling.value != null -> true + // Lockout is active, the UI layer should not have called this; skip the attempt. + lockout.value != null -> true // The input is too short; skip the attempt. input.isTooShort(authMethod) -> true // Auto-confirm attempt when the feature is not enabled; skip the attempt. @@ -216,22 +216,22 @@ constructor( ) } - // Check if we need to throttle and, if so, kick off the throttle countdown: - if (!authenticationResult.isSuccessful && authenticationResult.throttleDurationMs > 0) { - repository.setThrottleDuration( - durationMs = authenticationResult.throttleDurationMs, - ) - startThrottlingCountdown() + // Check if lockout should start and, if so, kick off the countdown: + if (!authenticationResult.isSuccessful && authenticationResult.lockoutDurationMs > 0) { + repository.apply { + setLockoutDuration(durationMs = authenticationResult.lockoutDurationMs) + reportLockoutStarted(durationMs = authenticationResult.lockoutDurationMs) + hasLockoutOccurred.value = true + } + startLockoutCountdown() } if (authenticationResult.isSuccessful) { - // Since authentication succeeded, we should refresh throttling to make sure that our - // state is completely reflecting the upstream source of truth. - refreshThrottling() + // Since authentication succeeded, refresh lockout to make sure the state is completely + // reflecting the upstream source of truth. + refreshLockout() - // Force a garbage collection in an attempt to erase any credentials left in memory. - // Do it after a 5-sec delay to avoid making the bouncer dismiss animation janky. - initiateGarbageCollection(delayMs = 5000) + repository.hasLockoutOccurred.value = false } return if (authenticationResult.isSuccessful) { @@ -249,52 +249,52 @@ constructor( } } - /** Starts refreshing the throttling state every second. */ - private suspend fun startThrottlingCountdown() { - cancelThrottlingCountdown() - throttlingCountdownJob = + /** Starts refreshing the lockout state every second. */ + private suspend fun startLockoutCountdown() { + cancelLockoutCountdown() + lockoutCountdownJob = applicationScope.launch { - while (refreshThrottling()) { + while (refreshLockout()) { delay(1.seconds.inWholeMilliseconds) } } } - /** Cancels any throttling state countdown started in [startThrottlingCountdown]. */ - private fun cancelThrottlingCountdown() { - throttlingCountdownJob?.cancel() - throttlingCountdownJob = null + /** Cancels any lockout state countdown started in [startLockoutCountdown]. */ + private fun cancelLockoutCountdown() { + lockoutCountdownJob?.cancel() + lockoutCountdownJob = null } /** Notifies that the currently-selected user has changed. */ private suspend fun onSelectedUserChanged() { - cancelThrottlingCountdown() - if (refreshThrottling()) { - startThrottlingCountdown() + cancelLockoutCountdown() + if (refreshLockout()) { + startLockoutCountdown() } } /** - * Refreshes the throttling state, hydrating the repository with the latest state. + * Refreshes the lockout state, hydrating the repository with the latest state. * - * @return Whether throttling is active or not. + * @return Whether lockout is active or not. */ - private suspend fun refreshThrottling(): Boolean { - withContext("$TAG#refreshThrottling", backgroundDispatcher) { + private suspend fun refreshLockout(): Boolean { + withContext("$TAG#refreshLockout", backgroundDispatcher) { val failedAttemptCount = async { repository.getFailedAuthenticationAttemptCount() } - val deadline = async { repository.getThrottlingEndTimestamp() } + val deadline = async { repository.getLockoutEndTimestamp() } val remainingMs = max(0, deadline.await() - clock.elapsedRealtime()) - repository.throttling.value = + repository.lockout.value = if (remainingMs > 0) { - AuthenticationThrottlingModel( + AuthenticationLockoutModel( failedAttemptCount = failedAttemptCount.await(), - remainingMs = remainingMs.toInt(), + remainingSeconds = ceil(remainingMs / 1000f).toInt(), ) } else { - null // Throttling ended. + null // Lockout ended. } } - return repository.throttling.value != null + return repository.lockout.value != null } private fun AuthenticationMethodModel.createCredential( @@ -315,15 +315,6 @@ constructor( } } - private suspend fun initiateGarbageCollection(delayMs: Long) { - withContext(backgroundDispatcher) { - delay(delayMs) - System.gc() - System.runFinalization() - System.gc() - } - } - companion object { const val TAG = "AuthenticationInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationLockoutModel.kt index d0d398e31859..8ee2d5e02bad 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationLockoutModel.kt @@ -16,17 +16,20 @@ package com.android.systemui.authentication.shared.model -/** Models a state for throttling the next authentication attempt. */ -data class AuthenticationThrottlingModel( +/** Models a state for temporarily locking out the next authentication attempt. */ +data class AuthenticationLockoutModel( - /** Number of failed authentication attempts so far. If not throttling this will be `0`. */ + /** Number of failed authentication attempts so far. If not locked out this will be `0`. */ val failedAttemptCount: Int = 0, /** - * Remaining amount of time, in milliseconds, before another authentication attempt can be done. - * If not throttling this will be `0`. + * Remaining amount of time, in seconds, before another authentication attempt can be done. If + * not locked out this will be `0`. * - * This number is changed throughout the timeout. + * This number is changed throughout the lockout. + * + * Note: this isn't precise (in milliseconds), but rounded up to ensure "at most" this amount of + * seconds remains. */ - val remainingMs: Int = 0, + val remainingSeconds: Int = 0, ) diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt index f2a3e74700db..addc75e52fad 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt @@ -21,5 +21,5 @@ data class AuthenticationResultModel( /** Whether authentication was successful. */ val isSuccessful: Boolean = false, /** If [isSuccessful] is `false`, how long the user must wait before trying again. */ - val throttleDurationMs: Int = 0, + val lockoutDurationMs: Int = 0, ) diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 24cd9b589639..b1a153aa86aa 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -66,8 +66,6 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { public static final int MODE_ON = 1; public static final int MODE_OFF = 2; public static final int MODE_ESTIMATE = 3; - @VisibleForTesting - public static final long LAYOUT_TRANSITION_DURATION = 200; private final AccessorizedBatteryDrawable mDrawable; private final ImageView mBatteryIconView; @@ -136,7 +134,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { private void setupLayoutTransition() { LayoutTransition transition = new LayoutTransition(); - transition.setDuration(LAYOUT_TRANSITION_DURATION); + transition.setDuration(200); // Animates appearing/disappearing of the battery percentage text using fade-in/fade-out // and disables all other animation types diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index a2ac66f6d831..63fe26a37e46 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -197,11 +197,42 @@ open class UdfpsKeyguardViewControllerLegacy( listenForGoneToAodTransition(this) listenForLockscreenAodTransitions(this) listenForAodToOccludedTransitions(this) + listenForAlternateBouncerToAodTransitions(this) + listenForDreamingToAodTransitions(this) } } } @VisibleForTesting + suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job { + return scope.launch { + transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect { + transitionStep -> + view.onDozeAmountChanged( + transitionStep.value, + transitionStep.value, + ANIMATE_APPEAR_ON_SCREEN_OFF, + ) + } + } + } + + @VisibleForTesting + suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job { + return scope.launch { + transitionInteractor + .transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD) + .collect { transitionStep -> + view.onDozeAmountChanged( + transitionStep.value, + transitionStep.value, + UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, + ) + } + } + } + + @VisibleForTesting suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job { return scope.launch { transitionInteractor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED).collect { @@ -246,7 +277,10 @@ open class UdfpsKeyguardViewControllerLegacy( suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { return scope.launch { transitionInteractor.dozeAmountTransition.collect { transitionStep -> - if (transitionStep.transitionState == TransitionState.CANCELED) { + if ( + transitionStep.from == KeyguardState.AOD && + transitionStep.transitionState == TransitionState.CANCELED + ) { if ( transitionInteractor.startedKeyguardTransitionStep.first().to != KeyguardState.AOD diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 1122877929a0..724c0fe1e4e4 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -19,8 +19,8 @@ package com.android.systemui.bouncer.domain.interactor import android.content.Context import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.domain.interactor.AuthenticationResult +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.classifier.FalsingClassifier import com.android.systemui.classifier.domain.interactor.FalsingInteractor @@ -32,7 +32,6 @@ import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableSharedFlow @@ -61,24 +60,25 @@ constructor( /** The user-facing message to show in the bouncer. */ val message: StateFlow<String?> = - combine(repository.message, authenticationInteractor.throttling) { message, throttling -> - messageOrThrottlingMessage(message, throttling) + combine(repository.message, authenticationInteractor.lockout) { message, lockout -> + messageOrLockoutMessage(message, lockout) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = - messageOrThrottlingMessage( + messageOrLockoutMessage( repository.message.value, - authenticationInteractor.throttling.value, + authenticationInteractor.lockout.value, ) ) /** - * The current authentication throttling state, set when the user has to wait before being able - * to try another authentication attempt. `null` indicates throttling isn't active. + * The current authentication lockout (aka "throttling") state, set when the user has to wait + * before being able to try another authentication attempt. `null` indicates lockout isn't + * active. */ - val throttling: StateFlow<AuthenticationThrottlingModel?> = authenticationInteractor.throttling + val lockout: StateFlow<AuthenticationLockoutModel?> = authenticationInteractor.lockout /** Whether the auto confirm feature is enabled for the currently-selected user. */ val isAutoConfirmEnabled: StateFlow<Boolean> = authenticationInteractor.isAutoConfirmEnabled @@ -103,9 +103,9 @@ constructor( init { if (flags.isEnabled()) { - // Clear the message if moved from throttling to no-longer throttling. + // Clear the message if moved from locked-out to no-longer locked-out. applicationScope.launch { - throttling.pairwise().collect { (previous, current) -> + lockout.pairwise().collect { (previous, current) -> if (previous != null && current == null) { clearMessage() } @@ -214,9 +214,9 @@ constructor( * Shows the error message. * * Callers should use this instead of [authenticate] when they know ahead of time that an auth - * attempt will fail but aren't interested in the other side effects like triggering throttling. + * attempt will fail but aren't interested in the other side effects like triggering lockout. * For example, if the user entered a pattern that's too short, the system can show the error - * message without having the attempt trigger throttling. + * message without having the attempt trigger lockout. */ private suspend fun showErrorMessage() { repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod())) @@ -251,15 +251,15 @@ constructor( } } - private fun messageOrThrottlingMessage( + private fun messageOrLockoutMessage( message: String?, - throttlingModel: AuthenticationThrottlingModel?, + lockoutModel: AuthenticationLockoutModel?, ): String { return when { - throttlingModel != null -> + lockoutModel != null -> applicationContext.getString( com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown, - throttlingModel.remainingMs.milliseconds.inWholeSeconds, + lockoutModel.remainingSeconds, ) message != null -> message else -> "" diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt index 5385442092b9..7f97718cb623 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt @@ -21,13 +21,13 @@ import androidx.annotation.VisibleForTesting /** Enumerates all known adaptive layout configurations. */ enum class BouncerSceneLayout { /** The default UI with the bouncer laid out normally. */ - STANDARD, + STANDARD_BOUNCER, /** The bouncer is displayed vertically stacked with the user switcher. */ - STACKED, + BELOW_USER_SWITCHER, /** The bouncer is displayed side-by-side with the user switcher or an empty space. */ - SIDE_BY_SIDE, + BESIDE_USER_SWITCHER, /** The bouncer is split in two with both sides shown side-by-side. */ - SPLIT, + SPLIT_BOUNCER, } /** Enumerates the supported window size classes. */ @@ -48,19 +48,19 @@ fun calculateLayoutInternal( isSideBySideSupported: Boolean, ): BouncerSceneLayout { return when (height) { - SizeClass.COMPACT -> BouncerSceneLayout.SPLIT + SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER SizeClass.MEDIUM -> when (width) { - SizeClass.COMPACT -> BouncerSceneLayout.STANDARD - SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD - SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE + SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER + SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD_BOUNCER + SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER } SizeClass.EXPANDED -> when (width) { - SizeClass.COMPACT -> BouncerSceneLayout.STANDARD - SizeClass.MEDIUM -> BouncerSceneLayout.STACKED - SizeClass.EXPANDED -> BouncerSceneLayout.SIDE_BY_SIDE + SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER + SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER + SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER } - }.takeIf { it != BouncerSceneLayout.SIDE_BY_SIDE || isSideBySideSupported } - ?: BouncerSceneLayout.STANDARD + }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isSideBySideSupported } + ?: BouncerSceneLayout.STANDARD_BOUNCER } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index e379dab918ef..0d7f6dcce1c7 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -50,12 +50,12 @@ sealed class AuthMethodBouncerViewModel( abstract val authenticationMethod: AuthenticationMethodModel /** - * String resource ID of the failure message to be shown during throttling. + * String resource ID of the failure message to be shown during lockout. * * The message must include 2 number parameters: the first one indicating how many unsuccessful - * attempts were made, and the second one indicating in how many seconds throttling will expire. + * attempts were made, and the second one indicating in how many seconds lockout will expire. */ - @get:StringRes abstract val throttlingMessageId: Int + @get:StringRes abstract val lockoutMessageId: Int /** Notifies that the UI has been shown to the user. */ fun onShown() { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 58fa85781ca1..4b1434323886 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -36,7 +36,6 @@ import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import com.android.systemui.user.ui.viewmodel.UserViewModel import dagger.Module import dagger.Provides -import kotlin.math.ceil import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob @@ -106,17 +105,17 @@ class BouncerViewModel( get() = bouncerInteractor.isUserSwitcherVisible private val isInputEnabled: StateFlow<Boolean> = - bouncerInteractor.throttling + bouncerInteractor.lockout .map { it == null } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = bouncerInteractor.throttling.value == null, + initialValue = bouncerInteractor.lockout.value == null, ) // Handle to the scope of the child ViewModel (stored in [authMethod]). private var childViewModelScope: CoroutineScope? = null - private val _throttlingDialogMessage = MutableStateFlow<String?>(null) + private val _dialogMessage = MutableStateFlow<String?>(null) /** View-model for the current UI, based on the current authentication method. */ val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> = @@ -129,20 +128,20 @@ class BouncerViewModel( ) /** - * A message for a throttling dialog to show when the user has attempted the wrong credential - * too many times and now must wait a while before attempting again. + * A message for a dialog to show when the user has attempted the wrong credential too many + * times and now must wait a while before attempting again. * * If `null`, no dialog should be shown. * - * Once the dialog is shown, the UI should call [onThrottlingDialogDismissed] when the user - * dismisses this dialog. + * Once the dialog is shown, the UI should call [onDialogDismissed] when the user dismisses this + * dialog. */ - val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() + val dialogMessage: StateFlow<String?> = _dialogMessage.asStateFlow() /** The user-facing message to show in the bouncer. */ val message: StateFlow<MessageViewModel> = - combine(bouncerInteractor.message, bouncerInteractor.throttling) { message, throttling -> - toMessageViewModel(message, isThrottled = throttling != null) + combine(bouncerInteractor.message, bouncerInteractor.lockout) { message, lockout -> + toMessageViewModel(message, isLockedOut = lockout != null) } .stateIn( scope = applicationScope, @@ -150,7 +149,7 @@ class BouncerViewModel( initialValue = toMessageViewModel( message = bouncerInteractor.message.value, - isThrottled = bouncerInteractor.throttling.value != null, + isLockedOut = bouncerInteractor.lockout.value != null, ), ) @@ -198,28 +197,28 @@ class BouncerViewModel( init { if (flags.isEnabled()) { applicationScope.launch { - combine(bouncerInteractor.throttling, authMethodViewModel) { - throttling, + combine(bouncerInteractor.lockout, authMethodViewModel) { + lockout, authMethodViewModel -> - if (throttling != null && authMethodViewModel != null) { + if (lockout != null && authMethodViewModel != null) { applicationContext.getString( - authMethodViewModel.throttlingMessageId, - throttling.failedAttemptCount, - ceil(throttling.remainingMs / 1000f).toInt(), + authMethodViewModel.lockoutMessageId, + lockout.failedAttemptCount, + lockout.remainingSeconds, ) } else { null } } .distinctUntilChanged() - .collect { dialogMessage -> _throttlingDialogMessage.value = dialogMessage } + .collect { dialogMessage -> _dialogMessage.value = dialogMessage } } } } - /** Notifies that a throttling dialog has been dismissed by the user. */ - fun onThrottlingDialogDismissed() { - _throttlingDialogMessage.value = null + /** Notifies that the dialog has been dismissed by the user. */ + fun onDialogDismissed() { + _dialogMessage.value = null } private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean { @@ -232,11 +231,11 @@ class BouncerViewModel( private fun toMessageViewModel( message: String?, - isThrottled: Boolean, + isLockedOut: Boolean, ): MessageViewModel { return MessageViewModel( text = message ?: "", - isUpdateAnimated = !isThrottled, + isUpdateAnimated = !isLockedOut, ) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index 3b7e32140560..b68271767fc2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -46,7 +46,7 @@ class PasswordBouncerViewModel( override val authenticationMethod = AuthenticationMethodModel.Password - override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message + override val lockoutMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message /** Whether the input method editor (for example, the software keyboard) is visible. */ private var isImeVisible: Boolean = false @@ -56,13 +56,13 @@ class PasswordBouncerViewModel( /** Whether the UI should request focus on the text field element. */ val isTextFieldFocusRequested = - combine(interactor.throttling, isTextFieldFocused) { throttling, hasFocus -> + combine(interactor.lockout, isTextFieldFocused) { throttling, hasFocus -> throttling == null && !hasFocus } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = interactor.throttling.value == null && !isTextFieldFocused.value, + initialValue = interactor.lockout.value == null && !isTextFieldFocused.value, ) override fun onHidden() { @@ -104,7 +104,7 @@ class PasswordBouncerViewModel( * hidden. */ suspend fun onImeVisibilityChanged(isVisible: Boolean) { - if (isImeVisible && !isVisible && interactor.throttling.value == null) { + if (isImeVisible && !isVisible && interactor.lockout.value == null) { interactor.onImeHiddenByUser() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index b1c5ab6122fe..69f8032ef4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -80,7 +80,7 @@ class PatternBouncerViewModel( override val authenticationMethod = AuthenticationMethodModel.Pattern - override val throttlingMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message + override val lockoutMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message /** Notifies that the user has started a drag gesture across the dot grid. */ fun onDragStart() { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index e25e82fe04c3..7f4a0296ebdc 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -104,7 +104,7 @@ class PinBouncerViewModel( override val authenticationMethod: AuthenticationMethodModel = authenticationMethod - override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message + override val lockoutMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message init { viewModelScope.launch { simBouncerInteractor.subId.collect { onResetSimFlow() } } diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt index fdd98bec0a2d..3063ebd60b0c 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/NotificationContainerBounds.kt @@ -18,8 +18,12 @@ package com.android.systemui.common.shared.model /** Models the bounds of the notification container. */ data class NotificationContainerBounds( + /** The position of the left of the container in its window coordinate system, in pixels. */ + val left: Float = 0f, /** The position of the top of the container in its window coordinate system, in pixels. */ val top: Float = 0f, + /** The position of the right of the container in its window coordinate system, in pixels. */ + val right: Float = 0f, /** The position of the bottom of the container in its window coordinate system, in pixels. */ val bottom: Float = 0f, /** Whether any modifications to top/bottom should be smoothly animated. */ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 0405ca43320f..ca8268dc89a3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -82,6 +82,7 @@ import com.android.systemui.qs.FgsManagerControllerImpl; import com.android.systemui.qs.QSFragmentStartableModule; import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; +import com.android.systemui.recordissue.RecordIssueModule; import com.android.systemui.retail.dagger.RetailModeModule; import com.android.systemui.scene.ui.view.WindowRootViewComponent; import com.android.systemui.screenrecord.ScreenRecordModule; @@ -209,6 +210,7 @@ import javax.inject.Named; PrivacyModule.class, QRCodeScannerModule.class, QSFragmentStartableModule.class, + RecordIssueModule.class, ReferenceModule.class, RetailModeModule.class, ScreenshotModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index cd764c0cc1a4..b91541813d62 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -1,6 +1,5 @@ package com.android.systemui.deviceentry -import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepositoryModule import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Module @@ -10,7 +9,6 @@ import dagger.multibindings.Multibinds includes = [ DeviceEntryRepositoryModule::class, - DeviceEntryHapticsRepositoryModule::class, ], ) abstract class DeviceEntryModule { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt deleted file mode 100644 index 1458404446e6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.deviceentry.data.repository - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** Interface for classes that can access device-entry haptics application state. */ -interface DeviceEntryHapticsRepository { - /** - * Whether a successful biometric haptic has been requested. Has not yet been handled if true. - */ - val successHapticRequest: Flow<Boolean> - - /** Whether an error biometric haptic has been requested. Has not yet been handled if true. */ - val errorHapticRequest: Flow<Boolean> - - fun requestSuccessHaptic() - fun handleSuccessHaptic() - fun requestErrorHaptic() - fun handleErrorHaptic() -} - -/** Encapsulates application state for device entry haptics. */ -@SysUISingleton -class DeviceEntryHapticsRepositoryImpl @Inject constructor() : DeviceEntryHapticsRepository { - private val _successHapticRequest = MutableStateFlow(false) - override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow() - - private val _errorHapticRequest = MutableStateFlow(false) - override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow() - - override fun requestSuccessHaptic() { - _successHapticRequest.value = true - } - - override fun handleSuccessHaptic() { - _successHapticRequest.value = false - } - - override fun requestErrorHaptic() { - _errorHapticRequest.value = true - } - - override fun handleErrorHaptic() { - _errorHapticRequest.value = false - } -} - -@Module -interface DeviceEntryHapticsRepositoryModule { - @Binds fun repository(impl: DeviceEntryHapticsRepositoryImpl): DeviceEntryHapticsRepository -} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index f27bbe6c7624..08e8c2d8271f 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -7,26 +7,36 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockModel +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.kotlin.sample import dagger.Binds import dagger.Module import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext /** Interface for classes that can access device-entry-related application state. */ interface DeviceEntryRepository { + /** Whether the device is immediately entering the device after a biometric unlock. */ + val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> + /** * Whether the device is unlocked. * @@ -73,7 +83,14 @@ constructor( private val lockPatternUtils: LockPatternUtils, private val keyguardBypassController: KeyguardBypassController, keyguardStateController: KeyguardStateController, + keyguardRepository: KeyguardRepository, ) : DeviceEntryRepository { + override val enteringDeviceFromBiometricUnlock = + keyguardRepository.biometricUnlockState + .filter { BiometricUnlockModel.dismissesKeyguard(it) } + .sample( + keyguardRepository.biometricUnlockSource.filterNotNull(), + ) private val _isUnlocked = MutableStateFlow(false) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt new file mode 100644 index 000000000000..1a6bd0427db5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map + +/** Business logic for device entry biometric states that may differ based on the biometric mode. */ +@ExperimentalCoroutinesApi +@SysUISingleton +class DeviceEntryBiometricAuthInteractor +@Inject +constructor( + biometricSettingsRepository: BiometricSettingsRepository, + deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, +) { + private val biometricMode: Flow<DeviceEntryBiometricMode> = + combine( + biometricSettingsRepository.isFingerprintEnrolledAndEnabled, + biometricSettingsRepository.isFaceAuthEnrolledAndEnabled, + ) { fingerprintEnrolled, faceEnrolled -> + if (fingerprintEnrolled && faceEnrolled) { + DeviceEntryBiometricMode.CO_EXPERIENCE + } else if (fingerprintEnrolled) { + DeviceEntryBiometricMode.FINGERPRINT_ONLY + } else if (faceEnrolled) { + DeviceEntryBiometricMode.FACE_ONLY + } else { + DeviceEntryBiometricMode.NONE + } + } + private val faceOnly: Flow<Boolean> = + biometricMode.map { it == DeviceEntryBiometricMode.FACE_ONLY } + + /** + * Triggered if face is the only biometric that can be used for device entry and a face failure + * occurs. + */ + val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> = + faceOnly.flatMapLatest { faceOnly -> + if (faceOnly) { + deviceEntryFaceAuthInteractor.faceFailure + } else { + emptyFlow() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt new file mode 100644 index 000000000000..70716c6c91fe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance + +@SysUISingleton +class DeviceEntryFaceAuthInteractor +@Inject +constructor( + repository: DeviceEntryFaceAuthRepository, +) { + val faceFailure: Flow<FailedFaceAuthenticationStatus> = + repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>() +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt new file mode 100644 index 000000000000..efa1c0a07490 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance + +@SysUISingleton +class DeviceEntryFingerprintAuthInteractor +@Inject +constructor( + repository: DeviceEntryFingerprintAuthRepository, +) { + val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> = + repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>() +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt index 53d6f737af8d..649a9715ffea 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt @@ -19,7 +19,6 @@ import com.android.keyguard.logging.BiometricUnlockLogger import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepository import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.power.domain.interactor.PowerInteractor @@ -34,11 +33,12 @@ import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart /** * Business logic for device entry haptic events. Determines whether the haptic should play. In - * particular, there are extra guards for whether device entry error and successes hatpics should + * particular, there are extra guards for whether device entry error and successes haptics should * play when the physical fingerprint sensor is located on the power button. */ @ExperimentalCoroutinesApi @@ -46,7 +46,9 @@ import kotlinx.coroutines.flow.onStart class DeviceEntryHapticsInteractor @Inject constructor( - private val repository: DeviceEntryHapticsRepository, + deviceEntryInteractor: DeviceEntryInteractor, + deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor, + deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor, fingerprintPropertyRepository: FingerprintPropertyRepository, biometricSettingsRepository: BiometricSettingsRepository, keyEventInteractor: KeyEventInteractor, @@ -77,9 +79,8 @@ constructor( emit(recentPowerButtonPressThresholdMs * -1L - 1L) } - val playSuccessHaptic: Flow<Boolean> = - repository.successHapticRequest - .filter { it } + val playSuccessHaptic: Flow<Unit> = + deviceEntryInteractor.enteringDeviceFromBiometricUnlock .sample( combine( powerButtonSideFpsEnrolled, @@ -88,7 +89,7 @@ constructor( ::Triple ) ) - .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> + .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) -> val sideFpsAllowsHaptic = !powerButtonDown && systemClock.uptimeMillis() - lastPowerButtonWakeup > @@ -96,38 +97,28 @@ constructor( val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic if (!allowHaptic) { logger.d("Skip success haptic. Recent power button press or button is down.") - handleSuccessHaptic() // immediately handle, don't vibrate } allowHaptic } - val playErrorHaptic: Flow<Boolean> = - repository.errorHapticRequest - .filter { it } + .map {} // map to Unit + + private val playErrorHapticForBiometricFailure: Flow<Unit> = + merge( + deviceEntryFingerprintAuthInteractor.fingerprintFailure, + deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure, + ) + .map {} // map to Unit + val playErrorHaptic: Flow<Unit> = + playErrorHapticForBiometricFailure .sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair)) - .map { (sideFpsEnrolled, powerButtonDown) -> + .filter { (sideFpsEnrolled, powerButtonDown) -> val allowHaptic = !sideFpsEnrolled || !powerButtonDown if (!allowHaptic) { logger.d("Skip error haptic. Power button is down.") - handleErrorHaptic() // immediately handle, don't vibrate } allowHaptic } - - fun vibrateSuccess() { - repository.requestSuccessHaptic() - } - - fun vibrateError() { - repository.requestErrorHaptic() - } - - fun handleSuccessHaptic() { - repository.handleSuccessHaptic() - } - - fun handleErrorHaptic() { - repository.handleErrorHaptic() - } + .map {} // map to Unit private val recentPowerButtonPressThresholdMs = 400L } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 4cddb9ccffdb..47be8ab0c0a2 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey @@ -30,6 +31,7 @@ import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest @@ -60,6 +62,9 @@ constructor( trustRepository: TrustRepository, flags: SceneContainerFlags, ) { + val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> = + repository.enteringDeviceFromBiometricUnlock + /** * Whether the device is unlocked. * diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt new file mode 100644 index 000000000000..6d885b349527 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryBiometricMode.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.shared + +/** Models the biometrics that can be used to enter the device. */ +enum class DeviceEntryBiometricMode { + /** No biometrics can be used to enter the device from the lockscreen. */ + NONE, + /** Only face can be used to enter the device from the lockscreen. */ + FACE_ONLY, + /** Only fingerprint can be used to enter the device from the lockscreen. */ + FINGERPRINT_ONLY, + /** Both face and fingerprint can be used to enter the device from the lockscreen. */ + CO_EXPERIENCE, +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java index 9c13a8c82b11..3fac865ffb1d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java @@ -43,7 +43,7 @@ public class DozeAuthRemover implements DozeMachine.Part { if (newState == DozeMachine.State.DOZE || newState == DozeMachine.State.DOZE_AOD) { int currentUser = mSelectedUserInteractor.getSelectedUserId(); if (mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(currentUser)) { - mKeyguardUpdateMonitor.clearBiometricRecognized(); + mKeyguardUpdateMonitor.clearFingerprintRecognized(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt b/packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt index 29f464285e5d..a97d5050421a 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.os.Trace import android.os.UserHandle import android.util.Log import com.android.systemui.broadcast.BroadcastDispatcher @@ -53,6 +54,8 @@ class LogBufferFreezer constructor( } private fun onBugreportStarted() { + Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", + "BUGREPORT_STARTED broadcast received") pendingToken?.run() Log.i(TAG, "Freezing log buffers") diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java index 87c12b4a5a59..72b08910ef32 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java @@ -25,7 +25,6 @@ import static com.android.systemui.flags.FlagManager.EXTRA_NAME; import static com.android.systemui.flags.FlagManager.EXTRA_VALUE; import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS; import static com.android.systemui.shared.Flags.exampleSharedFlag; - import static java.util.Objects.requireNonNull; import android.content.BroadcastReceiver; @@ -508,9 +507,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { enabled = isEnabled((ResourceBooleanFlag) f); overridden = readBooleanFlagOverride(f.getName()) != null; } else if (f instanceof SysPropBooleanFlag) { - // TODO(b/223379190): Teamfood not supported for sysprop flags yet. enabled = isEnabled((SysPropBooleanFlag) f); - teamfood = false; overridden = !mSystemProperties.get(f.getName()).isEmpty(); } else { // TODO: add support for other flag types. @@ -519,7 +516,7 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { } if (enabled) { - return new ReleasedFlag(f.getName(), f.getNamespace(), teamfood, overridden); + return new ReleasedFlag(f.getName(), f.getNamespace(), overridden); } else { return new UnreleasedFlag(f.getName(), f.getNamespace(), teamfood, overridden); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 6a0e88246027..d5b95d6721f9 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -16,7 +16,10 @@ package com.android.systemui.flags +import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR +import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor @@ -29,5 +32,9 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha override fun defineDependencies() { NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token + + val keyguardBottomAreaRefactor = FlagToken( + FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()) + KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index e8ceabf90af7..5a763b11d6a4 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -278,10 +278,6 @@ object Flags { "qs_user_detail_shortcut" ) - // TODO(b/296357483): Tracking Bug - @JvmField - val QS_PIPELINE_NEW_TILES = unreleasedFlag("qs_pipeline_new_tiles") - // TODO(b/254512383): Tracking Bug @JvmField val FULL_SCREEN_USER_SWITCHER = diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 5cc2e0aa811c..45433e62f214 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -60,6 +60,7 @@ import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -1052,6 +1053,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene if (ActivityManager.isUserAMonkey()) { return; } + Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", "BugReportAction#onPress"); // Add a little delay before executing, to give the // dialog a chance to go away before it takes a // screenshot. @@ -1065,6 +1067,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_PRESS); if (!mIActivityManager.launchBugReportHandlerApp()) { Log.w(TAG, "Bugreport handler could not be launched"); + Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", + "BugReportAction#requestingInteractiveBugReport"); mIActivityManager.requestInteractiveBugReport(); } } catch (RemoteException e) { @@ -1084,6 +1088,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene // Take a "full" bugreport. mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL); mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS); + Trace.instantForTrack(Trace.TRACE_TAG_APP, "bugreport", + "BugReportAction#requestingFullBugReport"); mIActivityManager.requestFullBugReport(); } catch (RemoteException e) { } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 30090874a480..b7260f2b2b94 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2611,14 +2611,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } if (mGoingToSleep) { - mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser); + mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser); Log.i(TAG, "Device is going to sleep, aborting keyguardDone"); return; } setPendingLock(false); // user may have authenticated during the screen off animation handleHide(); - mUpdateMonitor.clearBiometricRecognizedWhenKeyguardDone(currentUser); + mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index e47c44863980..eceaf6c3c4fd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -326,7 +326,7 @@ constructor( it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS }, ) - .flowOn(backgroundDispatcher) + .flowOn(mainDispatcher) // should revoke auth ASAP in the main thread .onEach { anyOfThemIsTrue -> if (anyOfThemIsTrue) { clearPendingAuthRequest("Resetting auth status") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt index fbd62cef7dfb..48b663493e59 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt @@ -28,7 +28,7 @@ import java.util.TreeMap import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow /** * Manages blueprint changes for the lockscreen. @@ -48,17 +48,14 @@ constructor( configurationRepository: ConfigurationRepository, blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>, ) { - private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = TreeMap() - private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1) - val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow() - + // This is TreeMap so that we can order the blueprints and assign numerical values to the + // blueprints in the adb tool. + private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = + TreeMap<String, KeyguardBlueprint>().apply { putAll(blueprints.associateBy { it.id }) } + val blueprint: MutableStateFlow<KeyguardBlueprint> = MutableStateFlow(blueprintIdMap[DEFAULT]!!) + val refreshBluePrint: MutableSharedFlow<Unit> = MutableSharedFlow(extraBufferCapacity = 1) val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange - init { - blueprintIdMap.putAll(blueprints.associateBy { it.id }) - applyBlueprint(blueprintIdMap[DEFAULT]!!) - } - /** * Emits the blueprint value to the collectors. * @@ -96,14 +93,17 @@ constructor( /** Emits the blueprint value to the collectors. */ fun applyBlueprint(blueprint: KeyguardBlueprint?) { - blueprint?.let { _blueprint.tryEmit(it) } + if (blueprint == this.blueprint.value) { + refreshBlueprint() + return + } + + blueprint?.let { this.blueprint.value = it } } /** Re-emits the last emitted blueprint value if possible. */ fun refreshBlueprint() { - if (_blueprint.replayCache.isNotEmpty()) { - _blueprint.tryEmit(_blueprint.replayCache.last()) - } + refreshBluePrint.tryEmit(Unit) } /** Prints all available blueprints to the PrintWriter. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 0b6b971f2314..7fdcf2f09bc1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -48,7 +48,7 @@ constructor( override fun start() { listenForDreamingToOccluded() listenForDreamingToGone() - listenForDreamingToDozing() + listenForDreamingToAodOrDozing() listenForTransitionToCamera(scope, keyguardInteractor) } @@ -94,7 +94,7 @@ constructor( } } - private fun listenForDreamingToDozing() { + private fun listenForDreamingToAodOrDozing() { scope.launch { combine( keyguardInteractor.dozeTransitionModel, @@ -102,11 +102,12 @@ constructor( ::Pair ) .collect { (dozeTransitionModel, keyguardState) -> - if ( - dozeTransitionModel.to == DozeStateModel.DOZE && - keyguardState == KeyguardState.DREAMING - ) { - startTransitionTo(KeyguardState.DOZING) + if (keyguardState == KeyguardState.DREAMING) { + if (dozeTransitionModel.to == DozeStateModel.DOZE) { + startTransitionTo(KeyguardState.DOZING) + } else if (dozeTransitionModel.to == DozeStateModel.DOZE_AOD) { + startTransitionTo(KeyguardState.AOD) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index cbfd17ff7ae4..9fe5c3f53d96 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -214,7 +214,7 @@ constructor( private fun listenForLockscreenToPrimaryBouncerDragging() { var transitionId: UUID? = null scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { - shadeRepository.shadeModel + shadeRepository.legacyShadeExpansion .sample( combine( transitionInteractor.startedKeyguardTransitionStep, @@ -224,23 +224,23 @@ constructor( ), ::toQuad ) - .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) -> + .collect { (shadeExpansion, keyguardState, statusBarState, isKeyguardUnlocked) -> val id = transitionId if (id != null) { if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) { // An existing `id` means a transition is started, and calls to // `updateTransition` will control it until FINISHED or CANCELED var nextState = - if (shadeModel.expansionAmount == 0f) { + if (shadeExpansion == 0f) { TransitionState.FINISHED - } else if (shadeModel.expansionAmount == 1f) { + } else if (shadeExpansion == 1f) { TransitionState.CANCELED } else { TransitionState.RUNNING } transitionRepository.updateTransition( id, - 1f - shadeModel.expansionAmount, + 1f - shadeExpansion, nextState, ) @@ -274,7 +274,7 @@ constructor( // integrated into KeyguardTransitionRepository if ( keyguardState.to == KeyguardState.LOCKSCREEN && - shadeModel.isUserDragging && + shadeRepository.legacyShadeTracking.value && !isKeyguardUnlocked && statusBarState == KEYGUARD ) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt index 7dab84dc7da3..ba44e6832161 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt @@ -21,11 +21,15 @@ import android.content.Context import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository +import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @@ -39,7 +43,18 @@ constructor( private val splitShadeStateController: SplitShadeStateController, ) { - val blueprint = keyguardBlueprintRepository.blueprint + /** + * The current blueprint for the lockscreen. + * + * This flow can also emit the same blueprint value if refreshBlueprint is emitted. + */ + val blueprint: Flow<KeyguardBlueprint> = + merge( + keyguardBlueprintRepository.blueprint, + keyguardBlueprintRepository.refreshBluePrint.map { + keyguardBlueprintRepository.blueprint.value + } + ) init { applicationScope.launch { @@ -91,4 +106,8 @@ constructor( fun refreshBlueprint() { keyguardBlueprintRepository.refreshBlueprint() } + + fun getCurrentBlueprint(): KeyguardBlueprint { + return keyguardBlueprintRepository.blueprint.value + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt index 5ed70b526f1b..046916aeb277 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt @@ -78,6 +78,9 @@ interface KeyguardFaceAuthInteractor { * flows. */ interface FaceAuthenticationListener { + /** Receive face isAuthenticated updates */ + fun onAuthenticatedChanged(isAuthenticated: Boolean) + /** Receive face authentication status updates */ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 702386d3b498..c12efe875b0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -224,8 +224,8 @@ constructor( configurationInteractor .dimensionPixelSize(R.dimen.keyguard_translate_distance_on_swipe_up) .flatMapLatest { translationDistance -> - shadeRepository.shadeModel.map { - if (it.expansionAmount == 0f) { + shadeRepository.legacyShadeExpansion.map { + if (it == 0f) { // Reset the translation value 0f } else { @@ -233,7 +233,7 @@ constructor( MathUtils.lerp( translationDistance, 0, - Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount) + Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it) ) } } 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 448411edb168..7882a9758105 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 @@ -46,6 +46,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController @@ -66,6 +67,7 @@ class KeyguardQuickAffordanceInteractor @Inject constructor( private val keyguardInteractor: KeyguardInteractor, + private val shadeInteractor: ShadeInteractor, private val lockPatternUtils: LockPatternUtils, private val keyguardStateController: KeyguardStateController, private val userTracker: UserTracker, @@ -100,9 +102,10 @@ constructor( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, keyguardInteractor.isKeyguardShowing, + shadeInteractor.anyExpansion, biometricSettingsRepository.isCurrentUserInLockdown, - ) { affordance, isDozing, isKeyguardShowing, isUserInLockdown -> - if (!isDozing && isKeyguardShowing && !isUserInLockdown) { + ) { affordance, isDozing, isKeyguardShowing, qsExpansion, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && (qsExpansion < 1.0f) && !isUserInLockdown) { affordance } else { KeyguardQuickAffordanceModel.Hidden @@ -117,10 +120,14 @@ constructor( * This is useful for experiences like the lock screen preview mode, where the affordances must * always be visible. */ - fun quickAffordanceAlwaysVisible( + suspend fun quickAffordanceAlwaysVisible( position: KeyguardQuickAffordancePosition, ): Flow<KeyguardQuickAffordanceModel> { - return quickAffordanceInternal(position) + return if (isFeatureDisabledByDevicePolicy()) { + flowOf(KeyguardQuickAffordanceModel.Hidden) + } else { + quickAffordanceInternal(position) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index 532df4afebf7..fb20000471a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -16,8 +16,10 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.trust.TrustManager import android.content.Context import android.hardware.biometrics.BiometricFaceConstants +import android.hardware.biometrics.BiometricSourceType import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.FaceWakeUpTriggersConfig import com.android.keyguard.KeyguardUpdateMonitor @@ -83,6 +85,7 @@ constructor( private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig, private val powerInteractor: PowerInteractor, private val biometricSettingsRepository: BiometricSettingsRepository, + private val trustManager: TrustManager, ) : CoreStartable, KeyguardFaceAuthInteractor { private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf() @@ -291,6 +294,20 @@ constructor( .onEach { running -> listeners.forEach { it.onRunningStateChanged(running) } } .flowOn(mainDispatcher) .launchIn(applicationScope) + repository.isAuthenticated + .sample(userRepository.selectedUserInfo, ::Pair) + .onEach { (isAuthenticated, userInfo) -> + if (!isAuthenticated) { + faceAuthenticationLogger.clearFaceRecognized() + trustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, userInfo.id) + } + } + .flowOn(backgroundDispatcher) + .onEach { (isAuthenticated, _) -> + listeners.forEach { it.onAuthenticatedChanged(isAuthenticated) } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) biometricSettingsRepository.isFaceAuthEnrolledAndEnabled .onEach { enrolledAndEnabled -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt index 8fe6309fc005..2ae5ce1ad545 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt @@ -51,9 +51,21 @@ enum class BiometricUnlockModel { companion object { private val wakeAndUnlockModes = setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING) + private val dismissesKeyguardModes = + setOf( + WAKE_AND_UNLOCK, + WAKE_AND_UNLOCK_PULSING, + UNLOCK_COLLAPSING, + WAKE_AND_UNLOCK_FROM_DREAM, + DISMISS_BOUNCER + ) fun isWakeAndUnlock(model: BiometricUnlockModel): Boolean { return wakeAndUnlockModes.contains(model) } + + fun dismissesKeyguard(model: BiometricUnlockModel): Boolean { + return dismissesKeyguardModes.contains(model) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt index 3c143fe1a68a..cc385a8eea85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt @@ -49,7 +49,7 @@ data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) : } /** Fingerprint authentication failed message. */ -object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus() +data object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus() /** Fingerprint authentication error message */ data class ErrorFingerprintAuthenticationStatus( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 4b4a19ecd770..dcf4284438bf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.content.res.ColorStateList +import android.view.HapticFeedbackConstants import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -30,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.VibratorHelper import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -51,6 +53,7 @@ object DeviceEntryIconViewBinder { fgViewModel: DeviceEntryForegroundViewModel, bgViewModel: DeviceEntryBackgroundViewModel, falsingManager: FalsingManager, + vibratorHelper: VibratorHelper, ) { DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() val longPressHandlingView = view.longPressHandlingView @@ -62,6 +65,10 @@ object DeviceEntryIconViewBinder { if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) { return } + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.CONFIRM, + ) viewModel.onLongPress() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index e603ead463f2..01a1ca3eeb93 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -68,7 +68,6 @@ import javax.inject.Provider import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -253,27 +252,21 @@ object KeyguardRootViewBinder { if (deviceEntryHapticsInteractor != null && vibratorHelper != null) { launch { - deviceEntryHapticsInteractor.playSuccessHaptic - .filter { it } - .collect { - vibratorHelper.performHapticFeedback( - view, - HapticFeedbackConstants.CONFIRM, - ) - deviceEntryHapticsInteractor.handleSuccessHaptic() - } + deviceEntryHapticsInteractor.playSuccessHaptic.collect { + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.CONFIRM, + ) + } } launch { - deviceEntryHapticsInteractor.playErrorHaptic - .filter { it } - .collect { - vibratorHelper.performHapticFeedback( - view, - HapticFeedbackConstants.REJECT, - ) - deviceEntryHapticsInteractor.handleErrorHaptic() - } + deviceEntryHapticsInteractor.playErrorHaptic.collect { + vibratorHelper.performHapticFeedback( + view, + HapticFeedbackConstants.REJECT, + ) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/BaseBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/BaseBlueprintTransition.kt index 42b1c1044cda..d0626d58a4ad 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/BaseBlueprintTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/BaseBlueprintTransition.kt @@ -24,6 +24,7 @@ import android.transition.TransitionValues import android.transition.Visibility import android.view.View import android.view.ViewGroup +import androidx.constraintlayout.helper.widget.Layer class BaseBlueprintTransition : TransitionSet() { init { @@ -31,6 +32,7 @@ class BaseBlueprintTransition : TransitionSet() { addTransition(AlphaOutVisibility()) .addTransition(ChangeBounds()) .addTransition(AlphaInVisibility()) + excludeTarget(Layer::class.java, /* exclude= */ true) } class AlphaOutVisibility : Visibility() { override fun onDisappear( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt index 96efb237047e..39a0547ded26 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt @@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.Notificatio import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.ui.SystemBarUtilsState import javax.inject.Inject import kotlinx.coroutines.DisposableHandle @@ -49,13 +49,13 @@ class AodNotificationIconsSection constructor( private val context: Context, private val configurationState: ConfigurationState, - private val configurationController: ConfigurationController, private val featureFlags: FeatureFlagsClassic, private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker, private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore, private val notificationIconAreaController: NotificationIconAreaController, private val smartspaceViewModel: KeyguardSmartspaceViewModel, + private val systemBarUtilsState: SystemBarUtilsState, ) : KeyguardSection() { private var nicBindingDisposable: DisposableHandle? = null @@ -89,11 +89,11 @@ constructor( if (NotificationIconContainerRefactor.isEnabled) { nicBindingDisposable?.dispose() nicBindingDisposable = - NotificationIconContainerViewBinder.bind( + NotificationIconContainerViewBinder.bindWhileAttached( nic, nicAodViewModel, configurationState, - configurationController, + systemBarUtilsState, iconBindingFailureTracker, nicAodIconViewStore, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 77ab9f416910..a693ec9317d0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -49,6 +49,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.gesture.TapGestureDetector import dagger.Lazy import javax.inject.Inject @@ -76,6 +77,7 @@ constructor( @Application private val scope: CoroutineScope, private val swipeUpAnywhereGestureHandler: Lazy<SwipeUpAnywhereGestureHandler>, private val tapGestureDetector: Lazy<TapGestureDetector>, + private val vibratorHelper: Lazy<VibratorHelper>, ) : KeyguardSection() { private val deviceEntryIconViewId = R.id.device_entry_icon_view private val alternateBouncerViewId = R.id.alternate_bouncer @@ -114,6 +116,7 @@ constructor( deviceEntryForegroundViewModel.get(), deviceEntryBackgroundViewModel.get(), falsingManager.get(), + vibratorHelper.get(), ) } constraintLayout.findViewById<FrameLayout?>(alternateBouncerViewId)?.let { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt index a64a422a1924..e7b6e44450bd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt @@ -26,7 +26,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R @@ -92,13 +91,7 @@ constructor( connect(R.id.nssl_placeholder, START, PARENT_ID, START) connect(R.id.nssl_placeholder, END, PARENT_ID, END) - val lockId = - if (DeviceEntryUdfpsRefactor.isEnabled) { - R.id.device_entry_icon_view - } else { - R.id.lock_icon_view - } - connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP) + addNotificationPlaceholderBarrier(this) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt index a25471cba66d..400d0dc2b242 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt @@ -20,7 +20,12 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context import android.view.View import android.view.ViewGroup +import androidx.constraintlayout.widget.Barrier import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.res.R @@ -54,6 +59,29 @@ constructor( private val placeHolderId = R.id.nssl_placeholder private var disposableHandle: DisposableHandle? = null + /** + * Align the notification placeholder bottom to the top of either the lock icon or the ambient + * indication area, whichever is higher. + */ + protected fun addNotificationPlaceholderBarrier(constraintSet: ConstraintSet) { + val lockId = + if (DeviceEntryUdfpsRefactor.isEnabled) { + R.id.device_entry_icon_view + } else { + R.id.lock_icon_view + } + + constraintSet.apply { + createBarrier( + R.id.nssl_placeholder_barrier_bottom, + Barrier.TOP, + 0, + *intArrayOf(lockId, R.id.ambient_indication_container) + ) + connect(R.id.nssl_placeholder, BOTTOM, R.id.nssl_placeholder_barrier_bottom, TOP) + } + } + override fun addViews(constraintLayout: ConstraintLayout) { if (!KeyguardShadeMigrationNssl.isEnabled) { return @@ -85,9 +113,9 @@ constructor( ) if (sceneContainerFlags.flexiNotifsEnabled()) { NotificationStackAppearanceViewBinder.bind( + context, sharedNotificationContainer, notificationStackAppearanceViewModel, - sceneContainerFlags, ambientState, controller, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt index 921fb3b1e79f..b0b5c81dd11c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt @@ -19,14 +19,12 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context import androidx.constraintlayout.widget.ConstraintSet -import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R @@ -87,18 +85,17 @@ constructor( ) setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin) } else { - connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, TOP, bottomMargin) + val splitShadeTopMargin = + context.resources.getDimensionPixelSize( + R.dimen.large_screen_shade_header_height + ) + connect(R.id.nssl_placeholder, TOP, PARENT_ID, TOP, splitShadeTopMargin) } + connect(R.id.nssl_placeholder, START, PARENT_ID, START) connect(R.id.nssl_placeholder, END, PARENT_ID, END) - val lockId = - if (DeviceEntryUdfpsRefactor.isEnabled) { - R.id.device_entry_icon_view - } else { - R.id.lock_icon_view - } - connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP) + addNotificationPlaceholderBarrier(this) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index bd6aae8f2dcb..f95713bf1802 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import android.animation.FloatEvaluator import android.animation.IntEvaluator import com.android.keyguard.KeyguardViewController -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.BurnInInteractor @@ -56,7 +55,6 @@ constructor( val shadeDependentFlows: ShadeDependentFlows, private val sceneContainerFlags: SceneContainerFlags, private val keyguardViewController: Lazy<KeyguardViewController>, - private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor, private val deviceEntryInteractor: DeviceEntryInteractor, ) { private val intEvaluator = IntEvaluator() @@ -182,8 +180,6 @@ constructor( } fun onLongPress() { - deviceEntryHapticsInteractor.vibrateSuccess() - // TODO (b/309804148): play auth ripple via an interactor if (sceneContainerFlags.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 4588e02df10e..1d4520ff8f03 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -111,10 +111,21 @@ constructor( /** An observable for the alpha level for the entire keyguard root view. */ val alpha: Flow<Float> = - merge( - keyguardInteractor.keyguardAlpha.distinctUntilChanged(), - occludedToLockscreenTransitionViewModel.lockscreenAlpha, - ) + combine( + keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) }, + merge( + keyguardInteractor.keyguardAlpha, + occludedToLockscreenTransitionViewModel.lockscreenAlpha, + ) + ) { transitionToGone, alpha -> + if (transitionToGone == 1f) { + // Ensures content is not visible when in GONE state + 0f + } else { + alpha + } + } + .distinctUntilChanged() private fun burnIn(): Flow<BurnInModel> { val dozingAmount: Flow<Float> = @@ -229,7 +240,9 @@ constructor( .distinctUntilChanged() fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) { - keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom)) + keyguardInteractor.setNotificationContainerBounds( + NotificationContainerBounds(top = top, bottom = bottom) + ) } /** Is there an expanded pulse, are we animating in response? */ diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index 8c5690b312ec..3c2facbc967a 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -132,6 +132,10 @@ constructor( logBuffer.log(TAG, DEBUG, "Face authentication failed") } + fun clearFaceRecognized() { + logBuffer.log(TAG, DEBUG, "Clear face recognized") + } + fun authenticationError( errorCode: Int, errString: CharSequence?, diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index dc55179f53f8..d8bb3e65392f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -188,7 +188,7 @@ public class LogModule { LogBufferFactory factory, QSPipelineFlagsRepository flags ) { - if (flags.getPipelineTilesEnabled()) { + if (flags.getTilesEnabled()) { // we use return factory.create("QSLog", 450 /* maxSize */, false /* systrace */); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 1fab58e18ad2..828d6ed6ab89 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -57,6 +57,8 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.settings.SecureSettings; +import dagger.Lazy; + import org.jetbrains.annotations.NotNull; import java.io.PrintWriter; @@ -73,8 +75,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Provider; -import dagger.Lazy; - /** Platform implementation of the quick settings tile host * * This class keeps track of the set of current tiles and is the in memory source of truth @@ -151,7 +151,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P mShadeController = shadeController; - if (featureFlags.getPipelineTilesEnabled()) { + if (featureFlags.getTilesEnabled()) { mQsFactories.add(newQsTileFactoryProvider.get()); } mQsFactories.add(defaultFactory); 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 4bda7307c667..5d28c8c0b69a 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 @@ -336,7 +336,7 @@ constructor( private suspend fun createTile(spec: TileSpec): QSTile? { val tile = withContext(mainDispatcher) { - if (featureFlags.pipelineTilesEnabled) { + if (featureFlags.tilesEnabled) { newQSTileFactory.get().createTile(spec.spec) } else { null diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt index 5c7420cb3c1b..935d07229ae5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt @@ -2,24 +2,18 @@ package com.android.systemui.qs.pipeline.shared import com.android.systemui.Flags as AconfigFlags import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.flags.Flags import com.android.systemui.flags.RefactorFlagUtils import javax.inject.Inject /** Encapsulate the different QS pipeline flags and their dependencies */ @SysUISingleton -class QSPipelineFlagsRepository -@Inject -constructor( - private val featureFlags: FeatureFlagsClassic, -) { +class QSPipelineFlagsRepository @Inject constructor() { + val pipelineEnabled: Boolean get() = AconfigFlags.qsNewPipeline() - /** @see Flags.QS_PIPELINE_NEW_TILES */ - val pipelineTilesEnabled: Boolean - get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_TILES) + val tilesEnabled: Boolean + get() = AconfigFlags.qsNewTiles() companion object Utils { fun assertInLegacyMode() = @@ -27,5 +21,11 @@ constructor( AconfigFlags.qsNewPipeline(), AconfigFlags.FLAG_QS_NEW_PIPELINE ) + + fun assertNewTilesInLegacyMode() = + RefactorFlagUtils.assertInLegacyMode( + AconfigFlags.qsNewTiles(), + AconfigFlags.FLAG_QS_NEW_TILES + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt new file mode 100644 index 000000000000..a4088f81f062 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles + +import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.service.quicksettings.Tile +import android.text.TextUtils +import android.view.View +import android.widget.Switch +import androidx.annotation.VisibleForTesting +import com.android.internal.logging.MetricsLogger +import com.android.systemui.Flags.recordIssueQsTile +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.res.R +import javax.inject.Inject + +class RecordIssueTile +@Inject +constructor( + host: QSHost, + uiEventLogger: QsEventLogger, + @Background backgroundLooper: Looper, + @Main mainHandler: Handler, + falsingManager: FalsingManager, + metricsLogger: MetricsLogger, + statusBarStateController: StatusBarStateController, + activityStarter: ActivityStarter, + qsLogger: QSLogger +) : + QSTileImpl<QSTile.BooleanState>( + host, + uiEventLogger, + backgroundLooper, + mainHandler, + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger + ) { + + @VisibleForTesting var isRecording: Boolean = false + + override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label) + + override fun isAvailable(): Boolean = recordIssueQsTile() + + override fun newTileState(): QSTile.BooleanState = + QSTile.BooleanState().apply { + label = tileLabel + handlesLongClick = false + } + + override fun handleClick(view: View?) { + isRecording = !isRecording + refreshState() + } + + override fun getLongClickIntent(): Intent? = null + + @VisibleForTesting + public override fun handleUpdateState(qsTileState: QSTile.BooleanState, arg: Any?) { + qsTileState.apply { + if (isRecording) { + value = true + state = Tile.STATE_ACTIVE + forceExpandIcon = false + secondaryLabel = mContext.getString(R.string.qs_record_issue_stop) + icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_on) + } else { + value = false + state = Tile.STATE_INACTIVE + forceExpandIcon = true + secondaryLabel = mContext.getString(R.string.qs_record_issue_start) + icon = ResourceIcon.get(R.drawable.qs_record_issue_icon_off) + } + label = tileLabel + contentDescription = + if (TextUtils.isEmpty(secondaryLabel)) label else "$label, $secondaryLabel" + expandedAccessibilityClassName = Switch::class.java.name + } + } + + companion object { + const val TILE_SPEC = "record_issue" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt index 27007bbf7aee..52e49f9d2653 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles.di import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.qs.QSFactory import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent @@ -44,6 +45,7 @@ constructor( ) : QSFactory { init { + QSPipelineFlagsRepository.assertNewTilesInLegacyMode() for (viewModelTileSpec in tileMap.keys) { require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) { "No config for $viewModelTileSpec" diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt new file mode 100644 index 000000000000..fc42ba495a51 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt @@ -0,0 +1,60 @@ +/* + * 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.tiles.impl.saver.domain + +import android.content.Context +import android.content.DialogInterface +import android.content.SharedPreferences +import android.os.Bundle +import com.android.internal.R +import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.DataSaverController +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class DataSaverDialogDelegate( + private val sysuiDialogFactory: SystemUIDialog.Factory, + private val context: Context, + private val backgroundContext: CoroutineContext, + private val dataSaverController: DataSaverController, + private val sharedPreferences: SharedPreferences, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return sysuiDialogFactory.create(this, context) + } + + override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setTitle(R.string.data_saver_enable_title) + setMessage(R.string.data_saver_description) + setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ -> + CoroutineScope(backgroundContext).launch { + dataSaverController.setDataSaverEnabled(true) + } + + sharedPreferences + .edit() + .putBoolean(DataSaverTileUserActionInteractor.DIALOG_SHOWN, true) + .apply() + } + setNeutralButton(R.string.cancel, null) + setShowForAllUsers(true) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt new file mode 100644 index 000000000000..25b09131522b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.saver.domain + +import android.content.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [DataSaverTileModel] to [QSTileState]. */ +class DataSaverTileMapper @Inject constructor(@Main private val resources: Resources) : + QSTileDataToStateMapper<DataSaverTileModel> { + override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState = + QSTileState.build(resources, config.uiConfig) { + with(data) { + if (isEnabled) { + activationState = QSTileState.ActivationState.ACTIVE + icon = { Icon.Resource(R.drawable.qs_data_saver_icon_on, null) } + secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2] + } else { + activationState = QSTileState.ActivationState.INACTIVE + icon = { Icon.Resource(R.drawable.qs_data_saver_icon_off, null) } + secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1] + } + contentDescription = label + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt new file mode 100644 index 000000000000..91e049b68c06 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt @@ -0,0 +1,52 @@ +/* + * 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.tiles.impl.saver.domain.interactor + +import android.os.UserHandle +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.statusbar.policy.DataSaverController +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +/** Observes data saver state changes providing the [DataSaverTileModel]. */ +class DataSaverTileDataInteractor +@Inject +constructor( + private val dataSaverController: DataSaverController, +) : QSTileDataInteractor<DataSaverTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<DataSaverTileModel> = + ConflatedCallbackFlow.conflatedCallbackFlow { + val initialValue = dataSaverController.isDataSaverEnabled + trySend(DataSaverTileModel(initialValue)) + + val callback = DataSaverController.Listener { trySend(DataSaverTileModel(it)) } + + dataSaverController.addCallback(callback) + awaitClose { dataSaverController.removeCallback(callback) } + } + + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt new file mode 100644 index 000000000000..af74409630ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.saver.domain.interactor + +import android.content.Context +import android.content.Intent +import android.provider.Settings +import com.android.internal.jank.InteractionJankMonitor +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverDialogDelegate +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.settings.UserFileManager +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.DataSaverController +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext + +/** Handles data saver tile clicks. */ +class DataSaverTileUserActionInteractor +@Inject +constructor( + @Application private val context: Context, + @Main private val coroutineContext: CoroutineContext, + @Background private val backgroundContext: CoroutineContext, + private val dataSaverController: DataSaverController, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + private val dialogLaunchAnimator: DialogLaunchAnimator, + private val systemUIDialogFactory: SystemUIDialog.Factory, + userFileManager: UserFileManager, +) : QSTileUserActionInteractor<DataSaverTileModel> { + companion object { + private const val INTERACTION_JANK_TAG = "start_data_saver" + const val PREFS = "data_saver" + const val DIALOG_SHOWN = "data_saver_dialog_shown" + } + + val sharedPreferences = + userFileManager.getSharedPreferences(PREFS, Context.MODE_PRIVATE, context.userId) + + override suspend fun handleInput(input: QSTileInput<DataSaverTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + val wasEnabled: Boolean = data.isEnabled + if (wasEnabled || sharedPreferences.getBoolean(DIALOG_SHOWN, false)) { + withContext(backgroundContext) { + dataSaverController.setDataSaverEnabled(!wasEnabled) + } + return@with + } + // Show a dialog to confirm first. Dialogs shown by the DialogLaunchAnimator + // must be created and shown on the main thread, so we post it to the UI + // handler + withContext(coroutineContext) { + val dialogContext = action.view?.context ?: context + val dialogDelegate = + DataSaverDialogDelegate( + systemUIDialogFactory, + dialogContext, + backgroundContext, + dataSaverController, + sharedPreferences + ) + val dialog = systemUIDialogFactory.create(dialogDelegate, dialogContext) + + if (action.view != null) { + dialogLaunchAnimator.showFromView( + dialog, + action.view!!, + DialogCuj( + InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, + INTERACTION_JANK_TAG + ) + ) + } else { + dialog.show() + } + } + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_DATA_SAVER_SETTINGS) + ) + } + } + } +} diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-sw600dp/config.xml b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/model/DataSaverTileModel.kt index be1f081d5b8f..040c7bf55236 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-sw600dp/config.xml +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/model/DataSaverTileModel.kt @@ -1,13 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright (c) 2023, The Android Open Source Project +/* + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<resources> - <!-- If true, attach the navigation bar to the app during app transition --> - <bool name="config_attachNavBarToAppDuringTransition">false</bool> -</resources> + +package com.android.systemui.qs.tiles.impl.saver.domain.model + +/** + * data saver tile model. + * + * @param isEnabled is true when the data saver is enabled; + */ +@JvmInline value class DataSaverTileModel(val isEnabled: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt new file mode 100644 index 000000000000..d67cf4d3d098 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.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.recordissue + +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.RecordIssueTile +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey + +@Module +interface RecordIssueModule { + /** Inject RecordIssueTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(RecordIssueTile.TILE_SPEC) + fun bindRecordIssueTile(recordIssueTile: RecordIssueTile): QSTileImpl<*> +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt index dbb58a329272..1156250666f3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt @@ -31,6 +31,7 @@ import com.android.systemui.flags.ReleasedFlag import com.android.systemui.flags.ResourceBooleanFlag import com.android.systemui.flags.UnreleasedFlag import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl +import com.android.systemui.media.controls.util.MediaInSceneContainerFlag import com.android.systemui.res.R import dagger.Module import dagger.Provides @@ -82,6 +83,10 @@ constructor( flagName = KeyguardShadeMigrationNssl.FLAG_NAME, flagValue = KeyguardShadeMigrationNssl.isEnabled, ), + AconfigFlagMustBeEnabled( + flagName = MediaInSceneContainerFlag.FLAG_NAME, + flagValue = MediaInSceneContainerFlag.isEnabled, + ), ) + classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } + listOf( diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java index c88549224183..c43d20cdf52f 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java @@ -35,6 +35,8 @@ import com.android.settingslib.RestrictedLockUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.res.R; +import java.util.Collections; + /** * {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}. * @@ -48,6 +50,7 @@ public class BrightnessSliderView extends FrameLayout { @Nullable private Drawable mProgressDrawable; private float mScale = 1f; + private final Rect mSystemGestureExclusionRect = new Rect(); public BrightnessSliderView(Context context) { this(context, null); @@ -176,6 +179,11 @@ public class BrightnessSliderView extends FrameLayout { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); applySliderScale(); + int horizontalMargin = + getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); + mSystemGestureExclusionRect.set(-horizontalMargin, 0, right - left + horizontalMargin, + bottom - top); + setSystemGestureExclusionRects(Collections.singletonList(mSystemGestureExclusionRect)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index e9779cd02760..5fbb60d76fbb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -59,6 +59,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.res.R; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.ui.view.WindowRootViewComponent; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.domain.interactor.ShadeInteractor; @@ -116,6 +117,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final AuthController mAuthController; private final Lazy<SelectedUserInteractor> mUserInteractor; private final Lazy<ShadeInteractor> mShadeInteractorLazy; + private final SceneContainerFlags mSceneContainerFlags; private ViewGroup mWindowRootView; private LayoutParams mLp; private boolean mHasTopUi; @@ -162,7 +164,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW Lazy<ShadeInteractor> shadeInteractorLazy, ShadeWindowLogger logger, Lazy<SelectedUserInteractor> userInteractor, - UserTracker userTracker) { + UserTracker userTracker, + SceneContainerFlags sceneContainerFlags) { mContext = context; mWindowRootViewComponentFactory = windowRootViewComponentFactory; mWindowManager = windowManager; @@ -180,6 +183,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW dumpManager.registerDumpable(this); mAuthController = authController; mUserInteractor = userInteractor; + mSceneContainerFlags = sceneContainerFlags; mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed(); mLockScreenDisplayTimeout = context.getResources() .getInteger(R.integer.config_lockScreenDisplayTimeout); @@ -287,6 +291,15 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + if (mSceneContainerFlags.isEnabled()) { + // This prevents the appearance and disappearance of the software keyboard (also known + // as the "IME") from scrolling/panning the window to make room for the keyboard. + // + // The scene container logic does its own adjustment and animation when the IME appears + // or disappears. + mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING; + } + mWindowManager.addView(mWindowRootView, mLp); mLpChanged.copyFrom(mLp); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index dd194eaade9b..8397caae438f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -794,13 +794,6 @@ public class QuickSettingsController implements Dumpable { /** update Qs height state */ public void setExpansionHeight(float height) { - // TODO(b/277909752): remove below log when bug is fixed - if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0 - && mBarState == SHADE) { - Log.wtf(TAG, - "setting QS height to 0 in split shade while shade is open(ing). " - + "Value of isExpandImmediate() = " + isExpandImmediate()); - } int maxHeight = getMaxExpansionHeight(); height = Math.min(Math.max( height, getMinExpansionHeight()), maxHeight); diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt index e94a3eb5db22..2445bdb17955 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt @@ -15,25 +15,14 @@ */ package com.android.systemui.shade.data.repository -import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.shade.ShadeExpansionChangeEvent -import com.android.systemui.shade.ShadeExpansionListener -import com.android.systemui.shade.ShadeExpansionStateManager -import com.android.systemui.shade.domain.model.ShadeModel import javax.inject.Inject -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.distinctUntilChanged +/** Data for the shade, mostly related to expansion of the shade and quick settings. */ interface ShadeRepository { - /** ShadeModel information regarding shade expansion events */ - val shadeModel: Flow<ShadeModel> - /** * Amount qs has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded. Quick * Settings can be expanded without the full shade expansion. @@ -167,34 +156,7 @@ interface ShadeRepository { /** Business logic for shade interactions */ @SysUISingleton -class ShadeRepositoryImpl -@Inject -constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepository { - override val shadeModel: Flow<ShadeModel> = - conflatedCallbackFlow { - val callback = - object : ShadeExpansionListener { - override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { - // Don't propagate ShadeExpansionChangeEvent.dragDownPxAmount field. - // It is too noisy and produces extra events that consumers won't care - // about - val info = - ShadeModel( - expansionAmount = event.fraction, - isExpanded = event.expanded, - isUserDragging = event.tracking - ) - trySendWithFailureLogging(info, TAG, "updated shade expansion info") - } - } - - val currentState = shadeExpansionStateManager.addExpansionListener(callback) - callback.onPanelExpansionChanged(currentState) - - awaitClose { shadeExpansionStateManager.removeExpansionListener(callback) } - } - .distinctUntilChanged() - +class ShadeRepositoryImpl @Inject constructor() : ShadeRepository { private val _qsExpansion = MutableStateFlow(0f) override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt deleted file mode 100644 index ce0f4283ff83..000000000000 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/model/ShadeModel.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ -package com.android.systemui.shade.domain.model - -import android.annotation.FloatRange - -/** Information about shade (NotificationPanel) expansion */ -data class ShadeModel( - /** 0 when collapsed, 1 when fully expanded. */ - @FloatRange(from = 0.0, to = 1.0) val expansionAmount: Float = 0f, - /** Whether the panel should be considered expanded */ - val isExpanded: Boolean = false, - /** Whether the user is actively dragging the panel. */ - val isUserDragging: Boolean = false, -) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index faffb3e118fc..d23c85a6d796 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; -import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; import android.view.accessibility.AccessibilityEvent; @@ -29,6 +28,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; +import com.android.systemui.util.time.SystemClock; import java.util.stream.Stream; @@ -39,21 +39,23 @@ import java.util.stream.Stream; */ public abstract class AlertingNotificationManager { private static final String TAG = "AlertNotifManager"; - protected final Clock mClock = new Clock(); + protected final SystemClock mSystemClock; protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>(); protected final HeadsUpManagerLogger mLogger; - public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler) { - mLogger = logger; - mHandler = handler; - } - protected int mMinimumDisplayTime; - protected int mStickyDisplayTime; - protected int mAutoDismissNotificationDecay; + protected int mStickyForSomeTimeAutoDismissTime; + protected int mAutoDismissTime; @VisibleForTesting public Handler mHandler; + public AlertingNotificationManager(HeadsUpManagerLogger logger, @Main Handler handler, + SystemClock systemClock) { + mLogger = logger; + mHandler = handler; + mSystemClock = systemClock; + } + /** * Called when posting a new notification that should alert the user and appear on screen. * Adds the notification to be managed. @@ -251,7 +253,7 @@ public abstract class AlertingNotificationManager { public long getEarliestRemovalTime(String key) { AlertEntry alerting = mAlertEntries.get(key); if (alerting != null) { - return Math.max(0, alerting.mEarliestRemovaltime - mClock.currentTimeMillis()); + return Math.max(0, alerting.mEarliestRemovalTime - mSystemClock.elapsedRealtime()); } return 0; } @@ -259,7 +261,7 @@ public abstract class AlertingNotificationManager { protected class AlertEntry implements Comparable<AlertEntry> { @Nullable public NotificationEntry mEntry; public long mPostTime; - public long mEarliestRemovaltime; + public long mEarliestRemovalTime; @Nullable protected Runnable mRemoveAlertRunnable; @@ -283,8 +285,8 @@ public abstract class AlertingNotificationManager { public void updateEntry(boolean updatePostTime, @Nullable String reason) { mLogger.logUpdateEntry(mEntry, updatePostTime, reason); - final long now = mClock.currentTimeMillis(); - mEarliestRemovaltime = now + mMinimumDisplayTime; + final long now = mSystemClock.elapsedRealtime(); + mEarliestRemovalTime = now + mMinimumDisplayTime; if (updatePostTime) { mPostTime = Math.max(mPostTime, now); @@ -318,7 +320,7 @@ public abstract class AlertingNotificationManager { * @return true if the notification has been on screen long enough */ public boolean wasShownLongEnough() { - return mEarliestRemovaltime < mClock.currentTimeMillis(); + return mEarliestRemovalTime < mSystemClock.elapsedRealtime(); } @Override @@ -351,7 +353,7 @@ public abstract class AlertingNotificationManager { if (mRemoveAlertRunnable != null) { removeAutoRemovalCallbacks("removeAsSoonAsPossible (will be rescheduled)"); - final long timeLeft = mEarliestRemovaltime - mClock.currentTimeMillis(); + final long timeLeft = mEarliestRemovalTime - mSystemClock.elapsedRealtime(); mHandler.postDelayed(mRemoveAlertRunnable, timeLeft); } } @@ -361,22 +363,16 @@ public abstract class AlertingNotificationManager { * @return the post time */ protected long calculatePostTime() { - return mClock.currentTimeMillis(); + return mSystemClock.elapsedRealtime(); } /** * @return When the notification should auto-dismiss itself, based on - * {@link SystemClock#elapsedRealTime()} + * {@link SystemClock#elapsedRealtime()} */ protected long calculateFinishTime() { // Overridden by HeadsUpManager HeadsUpEntry #calculateFinishTime return 0; } } - - protected final static class Clock { - public long currentTimeMillis() { - return SystemClock.elapsedRealtime(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt index a3adea0b86d9..642eaccc3c99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt @@ -35,6 +35,10 @@ import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileDataInteractor import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileUserActionInteractor import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel +import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverTileMapper +import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileDataInteractor +import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig @@ -85,6 +89,7 @@ interface ConnectivityModule { companion object { const val AIRPLANE_MODE_TILE_SPEC = "airplane" + const val DATA_SAVER_TILE_SPEC = "saver" /** Inject InternetTile or InternetTileNewImpl into tileMap in QSModule */ @Provides @@ -132,5 +137,36 @@ interface ConnectivityModule { stateInteractor, mapper, ) + + @Provides + @IntoMap + @StringKey(DATA_SAVER_TILE_SPEC) + fun provideDataSaverTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(DATA_SAVER_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_data_saver_icon_off, + labelRes = R.string.data_saver, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject DataSaverTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(DATA_SAVER_TILE_SPEC) + fun provideDataSaverTileViewModel( + factory: QSTileViewModelFactory.Static<DataSaverTileModel>, + mapper: DataSaverTileMapper, + stateInteractor: DataSaverTileDataInteractor, + userActionInteractor: DataSaverTileUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(DATA_SAVER_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index 22912df71334..85f4c366b370 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -20,6 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.statusbar.data.StatusBarDataLayerModule import com.android.systemui.statusbar.phone.LightBarController import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController +import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey @@ -33,7 +34,7 @@ import dagger.multibindings.IntoMap * ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule], * [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.). */ -@Module(includes = [StatusBarDataLayerModule::class]) +@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class]) abstract class StatusBarModule { @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index ecca9731f003..92391e7c76f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -22,7 +22,6 @@ import android.widget.FrameLayout import androidx.annotation.ColorInt import androidx.collection.ArrayMap import androidx.lifecycle.lifecycleScope -import com.android.internal.policy.SystemBarUtils import com.android.internal.statusbar.StatusBarIcon import com.android.internal.util.ContrastColorUtil import com.android.systemui.common.ui.ConfigurationState @@ -39,10 +38,8 @@ import com.android.systemui.statusbar.notification.icon.ui.viewmodel.Notificatio import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType import com.android.systemui.statusbar.phone.NotificationIconContainer -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.onConfigChanged +import com.android.systemui.statusbar.ui.SystemBarUtilsState import com.android.systemui.util.kotlin.mapValuesNotNullTo -import com.android.systemui.util.kotlin.stateFlow import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.stopAnimating import com.android.systemui.util.ui.value @@ -51,7 +48,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch @@ -59,20 +55,20 @@ import kotlinx.coroutines.launch /** Binds a view-model to a [NotificationIconContainer]. */ object NotificationIconContainerViewBinder { @JvmStatic - fun bind( + fun bindWhileAttached( view: NotificationIconContainer, viewModel: NotificationIconContainerShelfViewModel, configuration: ConfigurationState, - configurationController: ConfigurationController, + systemBarUtilsState: SystemBarUtilsState, failureTracker: StatusBarIconViewBindingFailureTracker, - viewStore: ShelfNotificationIconViewStore, + viewStore: IconViewStore, ): DisposableHandle { return view.repeatWhenAttached { lifecycleScope.launch { viewModel.icons.bindIcons( view, configuration, - configurationController, + systemBarUtilsState, notifyBindingFailures = { failureTracker.shelfFailures = it }, viewStore, ) @@ -81,66 +77,87 @@ object NotificationIconContainerViewBinder { } @JvmStatic - fun bind( + fun bindWhileAttached( view: NotificationIconContainer, viewModel: NotificationIconContainerStatusBarViewModel, configuration: ConfigurationState, - configurationController: ConfigurationController, + systemBarUtilsState: SystemBarUtilsState, failureTracker: StatusBarIconViewBindingFailureTracker, - viewStore: StatusBarNotificationIconViewStore, - ): DisposableHandle { - val contrastColorUtil = ContrastColorUtil.getInstance(view.context) - return view.repeatWhenAttached { - lifecycleScope.run { - launch { - val iconColors: Flow<NotificationIconColors> = - viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) } - viewModel.icons.bindIcons( - view, - configuration, - configurationController, - notifyBindingFailures = { failureTracker.statusBarFailures = it }, - viewStore, - ) { _, sbiv -> - StatusBarIconViewBinder.bindIconColors( - sbiv, - iconColors, - contrastColorUtil, - ) - } - } - launch { viewModel.bindIsolatedIcon(view, viewStore) } - launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) } + viewStore: IconViewStore, + ): DisposableHandle = + view.repeatWhenAttached { + lifecycleScope.launch { + bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore) + } + } + + suspend fun bind( + view: NotificationIconContainer, + viewModel: NotificationIconContainerStatusBarViewModel, + configuration: ConfigurationState, + systemBarUtilsState: SystemBarUtilsState, + failureTracker: StatusBarIconViewBindingFailureTracker, + viewStore: IconViewStore, + ): Unit = coroutineScope { + launch { + val contrastColorUtil = ContrastColorUtil.getInstance(view.context) + val iconColors: Flow<NotificationIconColors> = + viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) } + viewModel.icons.bindIcons( + view, + configuration, + systemBarUtilsState, + notifyBindingFailures = { failureTracker.statusBarFailures = it }, + viewStore, + ) { _, sbiv -> + StatusBarIconViewBinder.bindIconColors( + sbiv, + iconColors, + contrastColorUtil, + ) } } + launch { viewModel.bindIsolatedIcon(view, viewStore) } + launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) } } @JvmStatic - fun bind( + fun bindWhileAttached( view: NotificationIconContainer, viewModel: NotificationIconContainerAlwaysOnDisplayViewModel, configuration: ConfigurationState, - configurationController: ConfigurationController, + systemBarUtilsState: SystemBarUtilsState, failureTracker: StatusBarIconViewBindingFailureTracker, viewStore: IconViewStore, ): DisposableHandle { return view.repeatWhenAttached { lifecycleScope.launch { - view.setUseIncreasedIconScale(true) - launch { - viewModel.icons.bindIcons( - view, - configuration, - configurationController, - notifyBindingFailures = { failureTracker.aodFailures = it }, - viewStore, - ) { _, sbiv -> - viewModel.bindAodStatusBarIconView(sbiv, configuration) - } - } - launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) } + bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore) + } + } + } + + suspend fun bind( + view: NotificationIconContainer, + viewModel: NotificationIconContainerAlwaysOnDisplayViewModel, + configuration: ConfigurationState, + systemBarUtilsState: SystemBarUtilsState, + failureTracker: StatusBarIconViewBindingFailureTracker, + viewStore: IconViewStore, + ): Unit = coroutineScope { + view.setUseIncreasedIconScale(true) + launch { + viewModel.icons.bindIcons( + view, + configuration, + systemBarUtilsState, + notifyBindingFailures = { failureTracker.aodFailures = it }, + viewStore, + ) { _, sbiv -> + viewModel.bindAodStatusBarIconView(sbiv, configuration) } } + launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) } } private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView( @@ -199,7 +216,7 @@ object NotificationIconContainerViewBinder { private suspend fun Flow<NotificationIconsViewData>.bindIcons( view: NotificationIconContainer, configuration: ConfigurationState, - configurationController: ConfigurationController, + systemBarUtilsState: SystemBarUtilsState, notifyBindingFailures: (Collection<String>) -> Unit, viewStore: IconViewStore, bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> }, @@ -210,12 +227,8 @@ object NotificationIconContainerViewBinder { ) val iconHorizontalPaddingFlow: Flow<Int> = configuration.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) - val statusBarHeightFlow: StateFlow<Int> = - stateFlow(changedSignals = configurationController.onConfigChanged) { - SystemBarUtils.getStatusBarHeight(view.context) - } val layoutParams: Flow<FrameLayout.LayoutParams> = - combine(iconSizeFlow, iconHorizontalPaddingFlow, statusBarHeightFlow) { + combine(iconSizeFlow, iconHorizontalPaddingFlow, systemBarUtilsState.statusBarHeight) { iconSize, iconHPadding, statusBarHeight, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 5cdead407891..699e1406bc18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.row.ui.viewbinder.Activatable import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import com.android.systemui.statusbar.phone.NotificationIconAreaController -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.ui.SystemBarUtilsState import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.launch @@ -39,7 +39,7 @@ object NotificationShelfViewBinder { shelf: NotificationShelf, viewModel: NotificationShelfViewModel, configuration: ConfigurationState, - configurationController: ConfigurationController, + systemBarUtilsState: SystemBarUtilsState, falsingManager: FalsingManager, iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker, notificationIconAreaController: NotificationIconAreaController, @@ -48,11 +48,11 @@ object NotificationShelfViewBinder { ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) shelf.apply { if (NotificationIconContainerRefactor.isEnabled) { - NotificationIconContainerViewBinder.bind( + NotificationIconContainerViewBinder.bindWhileAttached( shelfIcons, viewModel.icons, configuration, - configurationController, + systemBarUtilsState, iconViewBindingFailureTracker, shelfIconViewStore, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt index abf09ae9844c..e78a694735e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt @@ -26,5 +26,8 @@ import kotlinx.coroutines.flow.MutableStateFlow @SysUISingleton class NotificationStackAppearanceRepository @Inject constructor() { /** The bounds of the notification stack in the current scene. */ - val stackBounds = MutableStateFlow(NotificationContainerBounds(0f, 0f)) + val stackBounds = MutableStateFlow(NotificationContainerBounds()) + + /** The corner radius of the notification stack, in dp. */ + val cornerRadiusDp = MutableStateFlow(32f) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt index 32e4e89c42c5..61a4dfcbd201 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt @@ -39,4 +39,7 @@ constructor( check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" } repository.stackBounds.value = bounds } + + /** The corner radius of the notification stack, in dp. */ + val cornerRadiusDp: StateFlow<Float> = repository.cornerRadiusDp.asStateFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt index c2c5eed6f013..adf6cca1ac65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt @@ -68,6 +68,8 @@ constructor( marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top), marginTopLargeScreen = getDimensionPixelSize(R.dimen.large_screen_shade_header_height), + keyguardSplitShadeTopMargin = + getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin), ) } } @@ -95,5 +97,6 @@ constructor( val marginBottom: Int, val marginTop: Int, val marginTopLargeScreen: Int, + val keyguardSplitShadeTopMargin: Int, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index a4e1a9c502f3..9373d497ffa7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel import com.android.systemui.statusbar.phone.NotificationIconAreaController -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.ui.SystemBarUtilsState import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.combine @@ -53,12 +53,12 @@ constructor( private val viewModel: NotificationListViewModel, @Background private val backgroundDispatcher: CoroutineDispatcher, private val configuration: ConfigurationState, - private val configurationController: ConfigurationController, private val falsingManager: FalsingManager, private val iconAreaController: NotificationIconAreaController, private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker, private val metricsLogger: MetricsLogger, private val shelfIconViewStore: ShelfNotificationIconViewStore, + private val systemBarUtilsState: SystemBarUtilsState, ) { fun bind( @@ -91,7 +91,7 @@ constructor( shelf, viewModel.shelf, configuration, - configurationController, + systemBarUtilsState, falsingManager, iconViewBindingFailureTracker, iconAreaController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt index fa7a8fdb7495..a9b542dcce2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt @@ -16,14 +16,16 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder +import android.content.Context +import android.util.TypedValue import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel +import kotlin.math.roundToInt import kotlinx.coroutines.launch /** Binds the shared notification container to its view-model. */ @@ -31,9 +33,9 @@ object NotificationStackAppearanceViewBinder { @JvmStatic fun bind( + context: Context, view: SharedNotificationContainer, viewModel: NotificationStackAppearanceViewModel, - sceneContainerFlags: SceneContainerFlags, ambientState: AmbientState, controller: NotificationStackScrollLayoutController, ) { @@ -45,6 +47,14 @@ object NotificationStackAppearanceViewBinder { bounds.top, controller.isAddOrRemoveAnimationPending ) + controller.setRoundedClippingBounds( + it.left, + it.top, + it.right, + it.bottom, + viewModel.cornerRadiusDp.value.dpToPx(context), + viewModel.cornerRadiusDp.value.dpToPx(context), + ) } } launch { @@ -56,4 +66,13 @@ object NotificationStackAppearanceViewBinder { } } } + + private fun Float.dpToPx(context: Context): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + this, + context.resources.displayMetrics + ) + .roundToInt() + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt index f4c0e92b0e87..834d3ffe63c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt @@ -23,6 +23,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */ @SysUISingleton @@ -37,4 +38,7 @@ constructor( /** The bounds of the notification stack in the current scene. */ val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds + + /** The corner radius of the notification stack, in dp. */ + val cornerRadiusDp: StateFlow<Float> = stackAppearanceInteractor.cornerRadiusDp } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt index c6fd98ea2223..9f22118e3332 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt @@ -24,6 +24,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow /** * ViewModel used by the Notification placeholders inside the scene container to update the @@ -55,9 +56,14 @@ constructor( * pixels. */ fun onBoundsChanged( + left: Float, top: Float, + right: Float, bottom: Float, ) { - interactor.setStackBounds(NotificationContainerBounds(top, bottom)) + interactor.setStackBounds(NotificationContainerBounds(left, top, right, bottom)) } + + /** The corner radius of the placeholder, in dp. */ + val cornerRadiusDp: StateFlow<Float> = interactor.cornerRadiusDp } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index b0f103827de2..9594bc3bfd86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -83,6 +83,14 @@ constructor( marginTop = if (it.useLargeScreenHeader) it.marginTopLargeScreen else it.marginTop, useSplitShade = it.useSplitShade, + paddingTop = + if (it.useSplitShade) { + // When in split shade, the margin is applied twice as the legacy shade + // code uses it to calculate padding. + it.keyguardSplitShadeTopMargin - 2 * it.marginTopLargeScreen + } else { + 0 + } ) } .distinctUntilChanged() @@ -156,11 +164,7 @@ constructor( ), ) { onLockscreen, bounds, config, (top, isInTransitionToAnyState, qsExpansion) -> if (onLockscreen) { - if (config.useSplitShade) { - bounds.copy(top = 0f) - } else { - bounds - } + bounds.copy(top = bounds.top + config.paddingTop) } else { // When QS expansion > 0, it should directly set the top padding so do not // animate it @@ -173,8 +177,8 @@ constructor( } .stateIn( scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = NotificationContainerBounds(0f, 0f), + started = SharingStarted.Lazily, + initialValue = NotificationContainerBounds(), ) val alpha: Flow<Float> = @@ -268,5 +272,6 @@ constructor( val marginEnd: Int, val marginBottom: Int, val useSplitShade: Boolean, + val paddingTop: Int, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 4e77801af515..97fc35a062f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -47,7 +47,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -180,8 +179,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private final SystemClock mSystemClock; private final boolean mOrderUnlockAndWake; private final Lazy<SelectedUserInteractor> mSelectedUserInteractor; - private final DeviceEntryHapticsInteractor mHapticsInteractor; - private long mLastFpFailureUptimeMillis; private int mNumConsecutiveFpFailures; @@ -288,7 +285,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp ScreenOffAnimationController screenOffAnimationController, VibratorHelper vibrator, SystemClock systemClock, - DeviceEntryHapticsInteractor hapticsInteractor, Lazy<SelectedUserInteractor> selectedUserInteractor, BiometricUnlockInteractor biometricUnlockInteractor ) { @@ -320,7 +316,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mSystemClock = systemClock; mOrderUnlockAndWake = resources.getBoolean( com.android.internal.R.bool.config_orderUnlockAndWake); - mHapticsInteractor = hapticsInteractor; mSelectedUserInteractor = selectedUserInteractor; dumpManager.registerDumpable(this); @@ -442,7 +437,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp if (mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) { - mHapticsInteractor.vibrateSuccess(); onBiometricUnlockedWithKeyguardDismissal(biometricSourceType); } startWakeAndUnlock(mode); @@ -726,14 +720,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } } - // Suppress all face auth errors if fingerprint can be used to authenticate - if ((biometricSourceType == BiometricSourceType.FACE - && !mUpdateMonitor.isUnlockWithFingerprintPossible( - mSelectedUserInteractor.get().getSelectedUserId())) - || (biometricSourceType == BiometricSourceType.FINGERPRINT)) { - mHapticsInteractor.vibrateError(); - } - cleanup(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 3a95e6d053e8..644c8962b93d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -49,6 +49,8 @@ import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; import java.io.PrintWriter; import java.util.ArrayList; @@ -115,11 +117,14 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp VisualStabilityProvider visualStabilityProvider, ConfigurationController configurationController, @Main Handler handler, + GlobalSettings globalSettings, + SystemClock systemClock, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger, JavaAdapter javaAdapter, ShadeInteractor shadeInteractor) { - super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger); + super(context, logger, handler, globalSettings, systemClock, accessibilityManagerWrapper, + uiEventLogger); Resources resources = mContext.getResources(); mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); statusBarStateController.addCallback(mStatusBarStateListener); @@ -206,7 +211,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUp @Override public boolean shouldSwallowClick(@NonNull String key) { BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); - return entry != null && mClock.currentTimeMillis() < entry.mPostTime; + return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime; } public void onExpandingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 49880d4475da..cd999349d055 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -74,8 +74,8 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.ui.SystemBarUtilsState; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -83,8 +83,6 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; -import kotlin.Unit; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -95,6 +93,8 @@ import java.util.concurrent.Executor; import javax.inject.Inject; +import kotlin.Unit; + /** * Contains the collapsed status bar and handles hiding/showing based on disable flags * and keyguard state. Also manages lifecycle to make sure the views it contains are being @@ -153,7 +153,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel; private final ConfigurationState mConfigurationState; - private final ConfigurationController mConfigurationController; + private final SystemBarUtilsState mSystemBarUtilsState; private final StatusBarNotificationIconViewStore mStatusBarIconViewStore; private final DemoModeController mDemoModeController; @@ -246,7 +246,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue KeyguardUpdateMonitor keyguardUpdateMonitor, NotificationIconContainerStatusBarViewModel statusBarIconsViewModel, ConfigurationState configurationState, - ConfigurationController configurationController, + SystemBarUtilsState systemBarUtilsState, StatusBarNotificationIconViewStore statusBarIconViewStore, DemoModeController demoModeController) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; @@ -275,7 +275,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mKeyguardUpdateMonitor = keyguardUpdateMonitor; mStatusBarIconsViewModel = statusBarIconsViewModel; mConfigurationState = configurationState; - mConfigurationController = configurationController; + mSystemBarUtilsState = systemBarUtilsState; mStatusBarIconViewStore = statusBarIconViewStore; mDemoModeController = demoModeController; } @@ -466,11 +466,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue .inflate(R.layout.notification_icon_area, notificationIconArea, true); NotificationIconContainer notificationIcons = notificationIconArea.requireViewById(R.id.notificationIcons); - NotificationIconContainerViewBinder.bind( + NotificationIconContainerViewBinder.bindWhileAttached( notificationIcons, mStatusBarIconsViewModel, mConfigurationState, - mConfigurationController, + mSystemBarUtilsState, mIconViewBindingFailureTracker, mStatusBarIconViewStore); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt index 99b123fbd702..ae58398753e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt @@ -161,13 +161,13 @@ constructor( if (it == null) { notConnectedFlow } else { - val secondary = it.contentDescription.toString() + val secondary = it.contentDescription flowOf( InternetTileModel.Active( - secondaryTitle = secondary, + secondaryLabel = secondary?.toText(), iconId = it.res, stateDescription = null, - contentDescription = ContentDescription.Loaded(secondary), + contentDescription = secondary, ) ) } @@ -241,5 +241,11 @@ constructor( string.substring(1, length - 1) } else string } + + private fun ContentDescription.toText(): Text = + when (this) { + is ContentDescription.Loaded -> Text.Loaded(this.description) + is ContentDescription.Resource -> Text.Resource(this.res) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index cec76f3140cd..8054b04529c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -25,8 +25,6 @@ import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; -import android.os.SystemClock; -import android.provider.Settings; import android.util.ArrayMap; import android.view.accessibility.AccessibilityManager; @@ -40,6 +38,8 @@ import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.util.ListenerSet; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; import java.io.PrintWriter; @@ -85,36 +85,40 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp public BaseHeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger, @Main Handler handler, + GlobalSettings globalSettings, + SystemClock systemClock, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger) { - super(logger, handler); + super(logger, handler, systemClock); mContext = context; mAccessibilityMgr = accessibilityManagerWrapper; mUiEventLogger = uiEventLogger; Resources resources = context.getResources(); mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); - mStickyDisplayTime = resources.getInteger(R.integer.sticky_heads_up_notification_time); - mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); + mStickyForSomeTimeAutoDismissTime = resources.getInteger( + R.integer.sticky_heads_up_notification_time); + mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay); mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); mSnoozedPackages = new ArrayMap<>(); int defaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); - mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), - SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs); + mSnoozeLengthMs = globalSettings.getInt(SETTING_HEADS_UP_SNOOZE_LENGTH_MS, + defaultSnoozeLengthMs); ContentObserver settingsObserver = new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { - final int packageSnoozeLengthMs = Settings.Global.getInt( - context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1); + final int packageSnoozeLengthMs = globalSettings.getInt( + SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1); if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) { mSnoozeLengthMs = packageSnoozeLengthMs; mLogger.logSnoozeLengthChange(packageSnoozeLengthMs); } } }; - context.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, + globalSettings.registerContentObserver( + globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), + /* notifyForDescendants = */ false, settingsObserver); } @@ -231,7 +235,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp final String key = snoozeKey(packageName, mUser); Long snoozedUntil = mSnoozedPackages.get(key); if (snoozedUntil != null) { - if (snoozedUntil > mClock.currentTimeMillis()) { + if (snoozedUntil > mSystemClock.elapsedRealtime()) { mLogger.logIsSnoozedReturned(key); return true; } @@ -250,7 +254,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp String packageName = entry.mEntry.getSbn().getPackageName(); String snoozeKey = snoozeKey(packageName, mUser); mLogger.logPackageSnoozed(snoozeKey); - mSnoozedPackages.put(snoozeKey, mClock.currentTimeMillis() + mSnoozeLengthMs); + mSnoozedPackages.put(snoozeKey, mSystemClock.elapsedRealtime() + mSnoozeLengthMs); } } @@ -308,7 +312,7 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp protected void dumpInternal(@NonNull PrintWriter pw, @NonNull String[] args) { pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); - pw.print(" now="); pw.println(mClock.currentTimeMillis()); + pw.print(" now="); pw.println(mSystemClock.elapsedRealtime()); pw.print(" mUser="); pw.println(mUser); for (AlertEntry entry: mAlertEntries.values()) { pw.print(" HeadsUpEntry="); pw.println(entry.mEntry); @@ -519,12 +523,12 @@ public abstract class BaseHeadsUpManager extends AlertingNotificationManager imp /** * @return When the notification should auto-dismiss itself, based on - * {@link SystemClock#elapsedRealTime()} + * {@link SystemClock#elapsedRealtime()} */ @Override protected long calculateFinishTime() { final long duration = getRecommendedHeadsUpTimeoutMs( - isStickyForSomeTime() ? mStickyDisplayTime : mAutoDismissNotificationDecay); + isStickyForSomeTime() ? mStickyForSomeTimeAutoDismissTime : mAutoDismissTime); return mPostTime + duration; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 80c68023b4c8..756c440eca89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -484,7 +484,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override - public void onBiometricsCleared() { + public void onFingerprintsCleared() { + update(false /* alwaysUpdate */); + } + + @Override + public void onFacesCleared() { update(false /* alwaysUpdate */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt new file mode 100644 index 000000000000..2b3fb70301e5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxy.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.ui + +import android.content.Context +import com.android.internal.policy.SystemBarUtils +import com.android.systemui.dagger.qualifiers.Application +import dagger.Binds +import javax.inject.Inject + +/** + * Proxy interface to [SystemBarUtils], allowing injection of different logic for testing. + * + * Developers should almost always prefer [SystemBarUtilsState] instead. + */ +interface SystemBarUtilsProxy { + fun getStatusBarHeight(): Int +} + +class SystemBarUtilsProxyImpl +@Inject +constructor( + @Application private val context: Context, +) : SystemBarUtilsProxy { + override fun getStatusBarHeight(): Int = SystemBarUtils.getStatusBarHeight(context) + + @dagger.Module + interface Module { + @Binds fun bindImpl(impl: SystemBarUtilsProxyImpl): SystemBarUtilsProxy + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt new file mode 100644 index 000000000000..ce811e205436 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/SystemBarUtilsState.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.ui + +import com.android.internal.policy.SystemBarUtils +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onConfigChanged +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +/** + * Tracks state from [SystemBarUtils]. Using this is both more efficient and more testable than + * using [SystemBarUtils] directly. + */ +class SystemBarUtilsState +@Inject +constructor( + configurationController: ConfigurationController, + proxy: SystemBarUtilsProxy, +) { + /** @see SystemBarUtils.getStatusBarHeight */ + val statusBarHeight: Flow<Int> = + configurationController.onConfigChanged + .onStart<Any> { emit(Unit) } + .map { proxy.getStatusBarHeight() } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index 968981197b83..50515daedc51 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -68,6 +68,7 @@ class UnfoldTransitionModule { @Provides @UnfoldBgProgressFlag + @Singleton fun unfoldBgProgressFlag() = Flags.unfoldAnimationBackgroundProgress() /** A globally available FoldStateListener that allows one to query the fold state. */ diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index 24917b37c8b1..88f63ad9c8cb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -63,7 +63,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.ui.SystemBarUtilsState; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -185,7 +185,7 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { mKeyguardSliceViewController, mNotificationIconAreaController, mSmartspaceController, - mock(ConfigurationController.class), + mock(SystemBarUtilsState.class), mock(ScreenOffAnimationController.class), mock(StatusBarIconViewBindingFailureTracker.class), mKeyguardUnlockAnimationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java index 95e21cf6138a..92b06ba3f714 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPAB import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; +import static android.view.WindowInsets.Type.systemBars; import static com.google.common.truth.Truth.assertThat; @@ -42,6 +43,7 @@ import android.annotation.IdRes; import android.content.Context; import android.content.pm.ActivityInfo; import android.database.ContentObserver; +import android.graphics.Insets; import android.graphics.Rect; import android.os.UserHandle; import android.provider.Settings; @@ -49,6 +51,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.widget.Button; @@ -59,10 +62,10 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener; +import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; import org.junit.After; @@ -314,6 +317,42 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { } @Test + public void onWindowBoundsChanged_updateDraggableWindowBounds() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mWindowMagnificationSettings.showSettingPanel(); + + // get the measured panel view frame size + final int panelWidth = mSettingView.getMeasuredWidth(); + final int panelHeight = mSettingView.getMeasuredHeight(); + + final Rect testWindowBounds = new Rect(10, 20, 1010, 2020); + final WindowInsets testWindowInsets = new WindowInsets.Builder() + .setInsetsIgnoringVisibility(systemBars(), Insets.of(100, 200, 100, 200)) + .build(); + mWindowManager.setWindowBounds(testWindowBounds); + mWindowManager.setWindowInsets(testWindowInsets); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mWindowMagnificationSettings.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + // the draggable window bounds left/top should be only related to the insets, + // and the bounds right/bottom should consider the panel frame size + // inset left (100) = 100 + int expectedLeft = 100; + // inset top (200) = 200 + int expectedTop = 200; + // window width (1010 - 10) - inset right (100) - panel width + int expectedRight = 900 - panelWidth; + // window height (2020 - 20) - inset bottom (200) - panel height + int expectedBottom = 1800 - panelHeight; + Rect expectedBounds = new Rect(expectedLeft, expectedTop, expectedRight, expectedBottom); + assertThat(mWindowMagnificationSettings.mDraggableWindowBounds).isEqualTo(expectedBounds); + } + + @Test public void onScreenSizeChanged_resetPositionToRightBottomCorner() { setupMagnificationCapabilityAndMode( /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java index 2a1cfd199f0c..215f93d1e163 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java @@ -17,7 +17,6 @@ package com.android.systemui.accessibility.floatingmenu; import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -26,9 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.PointF; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.annotations.EnableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -74,10 +71,6 @@ public class MenuAnimationControllerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); - @Rule - public final CheckFlagsRule mCheckFlagsRule = - DeviceFlagsValueProvider.createCheckFlagsRule(); - @Mock private AccessibilityManager mAccessibilityManager; @@ -233,7 +226,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) + @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) public void tuck_animates() { mMenuAnimationController.cancelAnimations(); mMenuAnimationController.moveToEdgeAndHide(); @@ -242,7 +235,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) + @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK) public void untuck_animates() { mMenuAnimationController.cancelAnimations(); mMenuAnimationController.moveOutEdgeAndShow(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java index 0f1364d951e7..be6f3ff8d3f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java @@ -21,11 +21,8 @@ import static android.view.View.VISIBLE; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; - import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex; - import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -41,10 +38,8 @@ import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -103,10 +98,6 @@ public class MenuViewLayerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); - @Rule - public final CheckFlagsRule mCheckFlagsRule = - DeviceFlagsValueProvider.createCheckFlagsRule(); - @Mock private IAccessibilityFloatingMenu mFloatingMenu; @@ -230,7 +221,7 @@ public class MenuViewLayerTest extends SysuiTestCase { } @Test - @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) + @DisableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme_old() { mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100)); final PointF beforePosition = mMenuView.getMenuPosition(); @@ -243,7 +234,7 @@ public class MenuViewLayerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) + @EnableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) public void showingImeInsetsChange_overlapOnIme_menuShownAboveIme() { mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 100)); final PointF beforePosition = mMenuView.getMenuPosition(); @@ -259,7 +250,7 @@ public class MenuViewLayerTest extends SysuiTestCase { } @Test - @RequiresFlagsDisabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) + @DisableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition_old() { mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200)); final PointF beforePosition = mMenuView.getMenuPosition(); @@ -271,7 +262,7 @@ public class MenuViewLayerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) + @EnableFlags(Flags.FLAG_FLOATING_MENU_IME_DISPLACEMENT_ANIMATION) public void hidingImeInsetsChange_overlapOnIme_menuBackToOriginalPosition() { mMenuAnimationController.moveAndPersistPosition(new PointF(0, IME_TOP + 200)); final PointF beforePosition = mMenuView.getMenuPosition(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java index 8f0a97ca06ba..8da6cf98d76f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java @@ -17,9 +17,7 @@ package com.android.systemui.accessibility.floatingmenu; import static android.app.UiModeManager.MODE_NIGHT_YES; - import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -27,9 +25,7 @@ import static org.mockito.Mockito.verify; import android.app.UiModeManager; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.annotations.EnableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.WindowManager; @@ -66,10 +62,6 @@ public class MenuViewTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); - @Rule - public final CheckFlagsRule mCheckFlagsRule = - DeviceFlagsValueProvider.createCheckFlagsRule(); - @Mock private AccessibilityManager mAccessibilityManager; @@ -147,7 +139,7 @@ public class MenuViewTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION) + @EnableFlags(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION) public void onEdgeChanged_startsRadiiAnimation() { final RadiiAnimator radiiAnimator = getRadiiAnimator(); mMenuView.onEdgeChanged(); @@ -155,7 +147,7 @@ public class MenuViewTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION) + @EnableFlags(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION) public void onDraggingStart_startsRadiiAnimation() { final RadiiAnimator radiiAnimator = getRadiiAnimator(); mMenuView.onDraggingStart(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt index 575d8bf8248c..fa176728619b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.runCurrent import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.model.ShadeModel import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.mock @@ -83,23 +82,13 @@ class DefaultUdfpsTouchOverlayViewModelTest : SysuiTestCase() { private fun TestComponent.shadeExpanded(expanded: Boolean) { if (expanded) { - shadeRepository.setShadeModel( - ShadeModel( - expansionAmount = 1f, - isExpanded = true, - isUserDragging = false, - ) - ) + shadeRepository.setLegacyShadeExpansion(1f) + shadeRepository.setLegacyShadeTracking(false) shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true) } else { keyguardRepository.setStatusBarState(StatusBarState.SHADE) - shadeRepository.setShadeModel( - ShadeModel( - expansionAmount = 0f, - isExpanded = false, - isUserDragging = false, - ) - ) + shadeRepository.setLegacyShadeExpansion(0f) + shadeRepository.setLegacyShadeTracking(false) shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt index f5f1622ac69b..863d9ebb41e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt @@ -20,7 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import com.android.systemui.keyguard.ui.viewmodel.deviceEntryIconViewModelTransitionsMock import com.android.systemui.kosmos.testScope @@ -49,7 +49,7 @@ import org.mockito.MockitoAnnotations class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() { val kosmos = testKosmos().apply { - featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) } + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) } } val testScope = kosmos.testScope diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index 094616f0682c..aa0d7b621793 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -35,7 +35,7 @@ import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.flags.SystemPropertiesHelper import com.android.systemui.keyguard.DismissCallbackRegistry @@ -62,7 +62,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq import org.mockito.Mock -import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -108,17 +107,18 @@ class BouncerMessageInteractorTest : SysuiTestCase() { suspend fun TestScope.init() { userRepository.setSelectedUserInfo(PRIMARY_USER) - val featureFlags = FakeFeatureFlags().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) } + val featureFlags = + FakeFeatureFlagsClassic().apply { set(Flags.REVAMPED_BOUNCER_MESSAGES, true) } primaryBouncerInteractor = PrimaryBouncerInteractor( bouncerRepository, - Mockito.mock(BouncerView::class.java), - Mockito.mock(Handler::class.java), - Mockito.mock(KeyguardStateController::class.java), - Mockito.mock(KeyguardSecurityModel::class.java), - Mockito.mock(PrimaryBouncerCallbackInteractor::class.java), - Mockito.mock(FalsingCollector::class.java), - Mockito.mock(DismissCallbackRegistry::class.java), + mock(BouncerView::class.java), + mock(Handler::class.java), + mock(KeyguardStateController::class.java), + mock(KeyguardSecurityModel::class.java), + mock(PrimaryBouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), context, keyguardUpdateMonitor, fakeTrustRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt index 395d7129bee3..ca9582240b93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt @@ -18,10 +18,10 @@ package com.android.systemui.bouncer.ui.helper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SIDE_BY_SIDE -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SPLIT -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STACKED -import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD +import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.BELOW_USER_SWITCHER +import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.BESIDE_USER_SWITCHER +import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SPLIT_BOUNCER +import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD_BOUNCER import com.google.common.truth.Truth.assertThat import java.util.Locale import org.junit.Test @@ -84,33 +84,33 @@ class BouncerSceneLayoutTest : SysuiTestCase() { listOf( Phone to Expected( - whenNaturallyHeld = STANDARD, - whenUnnaturallyHeld = SPLIT, + whenNaturallyHeld = STANDARD_BOUNCER, + whenUnnaturallyHeld = SPLIT_BOUNCER, ), Tablet to Expected( - whenNaturallyHeld = SIDE_BY_SIDE, - whenUnnaturallyHeld = STACKED, + whenNaturallyHeld = BESIDE_USER_SWITCHER, + whenUnnaturallyHeld = BELOW_USER_SWITCHER, ), Folded to Expected( - whenNaturallyHeld = STANDARD, - whenUnnaturallyHeld = SPLIT, + whenNaturallyHeld = STANDARD_BOUNCER, + whenUnnaturallyHeld = SPLIT_BOUNCER, ), Unfolded to Expected( - whenNaturallyHeld = SIDE_BY_SIDE, - whenUnnaturallyHeld = STANDARD, + whenNaturallyHeld = BESIDE_USER_SWITCHER, + whenUnnaturallyHeld = STANDARD_BOUNCER, ), TallerFolded to Expected( - whenNaturallyHeld = STANDARD, - whenUnnaturallyHeld = SPLIT, + whenNaturallyHeld = STANDARD_BOUNCER, + whenUnnaturallyHeld = SPLIT_BOUNCER, ), TallerUnfolded to Expected( - whenNaturallyHeld = SIDE_BY_SIDE, - whenUnnaturallyHeld = SIDE_BY_SIDE, + whenNaturallyHeld = BESIDE_USER_SWITCHER, + whenUnnaturallyHeld = BESIDE_USER_SWITCHER, ), ) .flatMap { (device, expected) -> @@ -124,13 +124,13 @@ class BouncerSceneLayoutTest : SysuiTestCase() { ) ) - if (expected.whenNaturallyHeld == SIDE_BY_SIDE) { + if (expected.whenNaturallyHeld == BESIDE_USER_SWITCHER) { add( TestCase( device = device, held = device.naturallyHeld, isSideBySideSupported = false, - expected = STANDARD, + expected = STANDARD_BOUNCER, ) ) } @@ -144,13 +144,13 @@ class BouncerSceneLayoutTest : SysuiTestCase() { ) ) - if (expected.whenUnnaturallyHeld == SIDE_BY_SIDE) { + if (expected.whenUnnaturallyHeld == BESIDE_USER_SWITCHER) { add( TestCase( device = device, held = device.naturallyHeld.flip(), isSideBySideSupported = false, - expected = STANDARD, + expected = STANDARD_BOUNCER, ) ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt index 9b8e581d1ba4..59bcf01e0eef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt @@ -18,157 +18,165 @@ package com.android.systemui.deviceentry.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.keyguard.logging.BiometricUnlockLogger import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor -import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository -import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor -import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor +import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource +import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus +import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus +import com.android.systemui.kosmos.testScope +import com.android.systemui.power.data.repository.powerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState -import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.testKosmos +import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.mock @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class DeviceEntryHapticsInteractorTest : SysuiTestCase() { - - private lateinit var repository: DeviceEntryHapticsRepository - private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository - private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - private lateinit var keyEventRepository: FakeKeyEventRepository - private lateinit var powerRepository: FakePowerRepository - private lateinit var systemClock: FakeSystemClock - private lateinit var underTest: DeviceEntryHapticsInteractor - - @Before - fun setUp() { - repository = DeviceEntryHapticsRepositoryImpl() - fingerprintPropertyRepository = FakeFingerprintPropertyRepository() - biometricSettingsRepository = FakeBiometricSettingsRepository() - keyEventRepository = FakeKeyEventRepository() - powerRepository = FakePowerRepository() - systemClock = FakeSystemClock() - underTest = - DeviceEntryHapticsInteractor( - repository = repository, - fingerprintPropertyRepository = fingerprintPropertyRepository, - biometricSettingsRepository = biometricSettingsRepository, - keyEventInteractor = KeyEventInteractor(keyEventRepository), - powerInteractor = - PowerInteractor( - powerRepository, - mock(FalsingCollector::class.java), - mock(ScreenOffAnimationController::class.java), - mock(StatusBarStateController::class.java), - ), - systemClock = systemClock, - logger = mock(BiometricUnlockLogger::class.java), - ) - } + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val underTest = kosmos.deviceEntryHapticsInteractor @Test - fun nonPowerButtonFPS_vibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isTrue() - } + fun nonPowerButtonFPS_vibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + runCurrent() + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNotNull() + } @Test - fun powerButtonFPS_vibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(false) - - // It's been 10 seconds since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000) - - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isTrue() - } + fun powerButtonFPS_vibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(false) + + // It's been 10 seconds since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNotNull() + } @Test - fun powerButtonFPS_powerDown_doNotVibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(true) // power button is currently DOWN - - // It's been 10 seconds since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000) + fun powerButtonFPS_powerDown_doNotVibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN + + // It's been 10 seconds since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 10000) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNull() + } - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isFalse() - } + @Test + fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = + testScope.runTest { + val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(false) + + // It's only been 50ms since the last power button wakeup + setAwakeFromPowerButton() + runCurrent() + kosmos.fakeSystemClock.setUptimeMillis(kosmos.fakeSystemClock.uptimeMillis() + 50) + + enterDeviceFromBiometricUnlock() + assertThat(playSuccessHaptic).isNull() + } @Test - fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = runTest { - val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(false) + fun nonPowerButtonFPS_vibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNotNull() + } - // It's only been 50ms since the last power button wakeup - setAwakeFromPowerButton() - runCurrent() - systemClock.setUptimeMillis(systemClock.uptimeMillis() + 50) + @Test + fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) + coExEnrolledAndEnabled() + runCurrent() + faceFailure() + assertThat(playErrorHaptic).isNull() + } - underTest.vibrateSuccess() - assertThat(playSuccessHaptic).isFalse() - } + @Test + fun powerButtonFPS_vibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNotNull() + } @Test - fun nonPowerButtonFPS_vibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC) - underTest.vibrateError() - assertThat(playErrorHaptic).isTrue() + fun powerButtonFPS_powerDown_doNotVibrateError() = + testScope.runTest { + val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) + setPowerButtonFingerprintProperty() + setFingerprintEnrolled() + kosmos.fakeKeyEventRepository.setPowerButtonDown(true) + runCurrent() + fingerprintFailure() + assertThat(playErrorHaptic).isNull() + } + + private suspend fun enterDeviceFromBiometricUnlock() { + kosmos.fakeDeviceEntryRepository.enteringDeviceFromBiometricUnlock( + BiometricUnlockSource.FINGERPRINT_SENSOR + ) } - @Test - fun powerButtonFPS_vibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - underTest.vibrateError() - assertThat(playErrorHaptic).isTrue() + private fun fingerprintFailure() { + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + FailFingerprintAuthenticationStatus + ) } - @Test - fun powerButtonFPS_powerDown_doNotVibrateError() = runTest { - val playErrorHaptic by collectLastValue(underTest.playErrorHaptic) - setPowerButtonFingerprintProperty() - setFingerprintEnrolled() - keyEventRepository.setPowerButtonDown(true) - underTest.vibrateError() - assertThat(playErrorHaptic).isFalse() + private fun faceFailure() { + kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus( + FailedFaceAuthenticationStatus() + ) } private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) { - fingerprintPropertyRepository.setProperties( + kosmos.fingerprintPropertyRepository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = fingerprintSensorType, @@ -181,15 +189,20 @@ class DeviceEntryHapticsInteractorTest : SysuiTestCase() { } private fun setFingerprintEnrolled() { - biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) + kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) } private fun setAwakeFromPowerButton() { - powerRepository.updateWakefulness( + kosmos.powerRepository.updateWakefulness( WakefulnessState.AWAKE, WakeSleepReason.POWER_BUTTON, WakeSleepReason.POWER_BUTTON, powerButtonLaunchGestureTriggered = false, ) } + + private fun coExEnrolledAndEnabled() { + setFingerprintEnrolled() + kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt index 42b0f5097cad..37c740968261 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.display.domain.interactor import android.companion.virtual.VirtualDeviceManager import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.Display @@ -75,7 +76,6 @@ class ConnectedDisplayInteractorTest : SysuiTestCase() { @Before fun setup() { - mSetFlagsRule.disableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR) whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt())).thenReturn(false) fakeKeyguardRepository.setKeyguardShowing(false) } @@ -160,9 +160,9 @@ class ConnectedDisplayInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR) fun displayState_virtualDeviceOwnedMirrorVirtualDisplay_connected() = testScope.runTest { - mSetFlagsRule.enableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR) whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt())) .thenReturn(true) val value by lastValue() @@ -183,9 +183,9 @@ class ConnectedDisplayInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR) fun virtualDeviceOwnedMirrorVirtualDisplay_emitsConnectedDisplayAddition() = testScope.runTest { - mSetFlagsRule.enableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR) whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt())) .thenReturn(true) var count = 0 diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt index a903d257d05f..523127e08a20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt @@ -21,6 +21,8 @@ import android.content.Intent import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources import android.content.res.Resources.NotFoundException +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD import com.android.systemui.SysuiTestCase @@ -68,15 +70,14 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { private val serverFlagReader = ServerFlagReaderFake() private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true) - private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true) + private val releasedFlagB = ReleasedFlag(name = "b", namespace = "test") @Before fun setup() { MockitoAnnotations.initMocks(this) - mSetFlagsRule.disableFlags(FLAG_SYSUI_TEAMFOOD) flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA) - flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB) + flagMap.put(releasedFlagB.name, releasedFlagB) mFeatureFlagsClassicDebug = FeatureFlagsClassicDebug( flagManager, @@ -99,7 +100,6 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { @Test fun readBooleanFlag() { - // Remember that the TEAMFOOD flag is id#1 and has special behavior. whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true) whenever(flagManager.readFlagValue<Boolean>(eq("4"), any())).thenReturn(false) @@ -122,9 +122,10 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_SYSUI_TEAMFOOD) fun teamFoodFlag_False() { assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isFalse() - assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue() + assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isTrue() // Regular boolean flags should still test the same. // Only our teamfoodableFlag should change. @@ -132,10 +133,10 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_SYSUI_TEAMFOOD) fun teamFoodFlag_True() { - mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD) assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue() - assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue() + assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isTrue() // Regular boolean flags should still test the same. // Only our teamfoodableFlag should change. @@ -143,14 +144,14 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_SYSUI_TEAMFOOD) fun teamFoodFlag_Overridden() { whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.name), any())) .thenReturn(true) - whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any())) + whenever(flagManager.readFlagValue<Boolean>(eq(releasedFlagB.name), any())) .thenReturn(false) - mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD) assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue() - assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse() + assertThat(mFeatureFlagsClassicDebug.isEnabled(releasedFlagB)).isFalse() // Regular boolean flags should still test the same. // Only our teamfoodableFlag should change. @@ -400,6 +401,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_SYSUI_TEAMFOOD) fun serverSide_OverrideUncached_NoRestart() { // No one has read the flag, so it's not in the cache. serverFlagReader.setFlagValue( @@ -411,6 +413,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_SYSUI_TEAMFOOD) fun serverSide_Override_Restarts() { // Read it to put it in the cache. mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA) @@ -423,6 +426,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_SYSUI_TEAMFOOD) fun serverSide_RedundantOverride_NoRestart() { // Read it to put it in the cache. mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA) 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 40c9432d543d..076d72513633 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -53,9 +53,11 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -101,6 +103,8 @@ class CustomizationProviderTest : SysuiTestCase() { private lateinit var underTest: CustomizationProvider private lateinit var testScope: TestScope + private val kosmos = testKosmos() + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -185,6 +189,7 @@ class CustomizationProviderTest : SysuiTestCase() { }, ) .keyguardInteractor, + shadeInteractor = kosmos.shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d246f0e49e1c..ae5f625b1c8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -97,6 +97,7 @@ import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransition import com.android.systemui.log.SessionTracker; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.scene.FakeWindowRootViewComponent; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.ui.view.WindowRootView; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -214,6 +215,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock CoroutineDispatcher mDispatcher; private @Mock DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; private @Mock SystemPropertiesHelper mSystemPropertiesHelper; + private @Mock SceneContainerFlags mSceneContainerFlags; private FakeFeatureFlags mFeatureFlags; private final int mDefaultUserId = 100; @@ -258,7 +260,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor, - mUserTracker); + mUserTracker, + mSceneContainerFlags); mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false); mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt index 5852bdb5c351..f2bd817af44d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt @@ -15,6 +15,8 @@ * */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.keyguard.data.repository import androidx.test.filters.SmallTest @@ -25,8 +27,10 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBl import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -76,8 +80,21 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() { fun testTransitionToLayout_validId() { assertThat(underTest.applyBlueprint(DEFAULT)).isTrue() } + @Test fun testTransitionToLayout_invalidId() { assertThat(underTest.applyBlueprint("abc")).isFalse() } + + @Test + fun testTransitionToSameBlueprint_refreshesBlueprint() = + testScope.runTest { + val refreshBlueprint by collectLastValue(underTest.refreshBluePrint) + runCurrent() + + underTest.applyBlueprint(defaultLockscreenBlueprint) + runCurrent() + + assertThat(refreshBlueprint).isNotNull() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index de12b8f91d20..4ab8e28bc232 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -17,8 +17,10 @@ package com.android.systemui.keyguard.domain.interactor +import android.app.trust.TrustManager import android.content.pm.UserInfo import android.hardware.biometrics.BiometricFaceConstants +import android.hardware.biometrics.BiometricSourceType import android.os.Handler import android.os.PowerManager import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -62,6 +64,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.domain.interactor.SelectedUserInteractor +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -74,8 +77,11 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @@ -99,7 +105,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig - @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var trustManager: TrustManager @Before fun setup() { @@ -146,7 +153,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { keyguardUpdateMonitor, FakeTrustRepository(), testScope.backgroundScope, - mSelectedUserInteractor, + selectedUserInteractor, underTest, ) }, @@ -169,6 +176,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { faceWakeUpTriggersConfig, powerInteractor, fakeBiometricSettingsRepository, + trustManager, ) } @@ -498,6 +506,22 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { assertThat(faceAuthRepository.isLockedOut.value).isTrue() } + @Test + fun whenIsAuthenticatedFalse_clearFaceBiometrics() = + testScope.runTest { + underTest.start() + + faceAuthRepository.isAuthenticated.value = true + runCurrent() + verify(trustManager, never()) + .clearAllBiometricRecognized(eq(BiometricSourceType.FACE), anyInt()) + + faceAuthRepository.isAuthenticated.value = false + runCurrent() + + verify(trustManager).clearAllBiometricRecognized(eq(BiometricSourceType.FACE), anyInt()) + } + companion object { private const val primaryUserId = 1 private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY) 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 66c8a229f0a1..b4ae7e3a7ca9 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 @@ -46,7 +46,9 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -242,6 +244,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var userTracker: UserTracker + private val kosmos = testKosmos() + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -311,6 +315,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { featureFlags = featureFlags, ) .keyguardInteractor, + shadeInteractor = kosmos.shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index bf6d5c4535ec..b8a8bdf06954 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -43,7 +43,6 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.shade.domain.model.ShadeModel import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -1138,7 +1137,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { runCurrent() // WHEN primary bouncer shows - bouncerRepository.setPrimaryShow(true) // beverlyt + bouncerRepository.setPrimaryShow(true) runCurrent() val info = @@ -1233,6 +1232,36 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { } @Test + fun dreamingToAod() = + testScope.runTest { + // GIVEN a prior transition has run to DREAMING + keyguardRepository.setDreaming(true) + runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING) + runCurrent() + + // WHEN the device starts DOZE_AOD + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + from = DozeStateModel.INITIALIZED, + to = DozeStateModel.DOZE_AOD, + ) + ) + runCurrent() + + val info = + withArgCaptor<TransitionInfo> { + verify(transitionRepository).startTransition(capture()) + } + // THEN a transition to AOD should occur + assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor") + assertThat(info.from).isEqualTo(KeyguardState.DREAMING) + assertThat(info.to).isEqualTo(KeyguardState.AOD) + assertThat(info.animator).isNotNull() + + coroutineContext.cancelChildren() + } + + @Test fun lockscreenToOccluded() = testScope.runTest { // GIVEN a prior transition has run to LOCKSCREEN @@ -1329,12 +1358,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // GIVEN the keyguard is showing locked keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) runCurrent() - shadeRepository.setShadeModel( - ShadeModel( - expansionAmount = .9f, - isUserDragging = true, - ) - ) + shadeRepository.setLegacyShadeTracking(true) + shadeRepository.setLegacyShadeExpansion(.9f) runCurrent() // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur @@ -1350,12 +1375,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { // WHEN the user stops dragging and shade is back to expanded clearInvocations(transitionRepository) runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER) - shadeRepository.setShadeModel( - ShadeModel( - expansionAmount = 1f, - isUserDragging = false, - ) - ) + shadeRepository.setLegacyShadeTracking(false) + shadeRepository.setLegacyShadeExpansion(1f) runCurrent() // THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt index 67fba42aac5b..22569e27d02a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.gesture.TapGestureDetector import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -91,6 +92,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { TestScope().backgroundScope, { mock(SwipeUpAnywhereGestureHandler::class.java) }, { mock(TapGestureDetector::class.java) }, + { mock(VibratorHelper::class.java) }, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt index c9b14a41edca..daafe12514ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt @@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER @@ -55,7 +55,7 @@ class BouncerToGoneFlowsTest : SysuiTestCase() { private val kosmos = testKosmos().apply { - featureFlagsClassic.apply { + fakeFeatureFlagsClassic.apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) set(Flags.FULL_SCREEN_USER_SWITCHER, false) } 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 1584be0ab565..af38523c2fd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -54,9 +54,11 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -108,6 +110,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { private lateinit var dockManager: DockManagerFake private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository + private val kosmos = testKosmos() + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -221,6 +225,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { quickAffordanceInteractor = KeyguardQuickAffordanceInteractor( keyguardInteractor = keyguardInteractor, + shadeInteractor = kosmos.shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 0c30d10ea563..b6a661be8c74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -264,12 +264,14 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha) .thenReturn(emptyFlow()) whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow) + whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f)) underTest = KeyguardQuickAffordancesCombinedViewModel( quickAffordanceInteractor = KeyguardQuickAffordanceInteractor( keyguardInteractor = keyguardInteractor, + shadeInteractor = shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 58624d356e60..687800714e05 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -144,19 +144,43 @@ class KeyguardRootViewModelTest : SysuiTestCase() { @Test fun alpha() = testScope.runTest { - val value = collectLastValue(underTest.alpha) - assertThat(value()).isEqualTo(0f) + val alpha by collectLastValue(underTest.alpha) + + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, + testScope = testScope, + ) repository.setKeyguardAlpha(0.1f) - assertThat(value()).isEqualTo(0.1f) + assertThat(alpha).isEqualTo(0.1f) repository.setKeyguardAlpha(0.5f) - assertThat(value()).isEqualTo(0.5f) + assertThat(alpha).isEqualTo(0.5f) repository.setKeyguardAlpha(0.2f) - assertThat(value()).isEqualTo(0.2f) + assertThat(alpha).isEqualTo(0.2f) repository.setKeyguardAlpha(0f) - assertThat(value()).isEqualTo(0f) + assertThat(alpha).isEqualTo(0f) occludedToLockscreenAlpha.value = 0.8f - assertThat(value()).isEqualTo(0.8f) + assertThat(alpha).isEqualTo(0.8f) + } + + @Test + fun alphaWhenGoneEqualsZero() = + testScope.runTest { + val alpha by collectLastValue(underTest.alpha) + + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + testScope = testScope, + ) + + repository.setKeyguardAlpha(0.1f) + assertThat(alpha).isEqualTo(0f) + repository.setKeyguardAlpha(0.5f) + assertThat(alpha).isEqualTo(0f) + repository.setKeyguardAlpha(1f) + assertThat(alpha).isEqualTo(0f) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt index c15a2c6a3df7..e139466c8096 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt @@ -23,7 +23,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -47,7 +47,9 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LockscreenToAodTransitionViewModelTest : SysuiTestCase() { private val kosmos = - testKosmos().apply { featureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) } } + testKosmos().apply { + fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) } + } private val testScope = kosmos.testScope private val repository = kosmos.fakeKeyguardTransitionRepository private val shadeRepository = kosmos.shadeRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt index b31968c79647..7a564aca00bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt @@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -45,7 +45,7 @@ import org.junit.runner.RunWith class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().apply { - featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } } private val testScope = kosmos.testScope private val repository = kosmos.fakeKeyguardTransitionRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 5245b224fa5b..5e2423a8b373 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -18,6 +18,7 @@ package com.android.systemui.qs; import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE; +import static com.android.systemui.Flags.FLAG_QS_NEW_TILES; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -53,7 +54,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.nano.SystemUIProtoDump; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.qs.QSFactory; @@ -79,6 +79,8 @@ import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -93,8 +95,6 @@ import java.util.concurrent.Executor; import javax.inject.Provider; -import dagger.Lazy; - @RunWith(AndroidTestingRunner.class) @SmallTest public class QSTileHostTest extends SysuiTestCase { @@ -147,9 +147,8 @@ public class QSTileHostTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE); - // TODO(b/299909337): Add test checking the new factory is used when the flag is on - mFeatureFlags.set(Flags.QS_PIPELINE_NEW_TILES, false); - mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags); + mSetFlagsRule.disableFlags(FLAG_QS_NEW_TILES); + mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(); mMainExecutor = new FakeExecutor(new FakeSystemClock()); @@ -704,7 +703,7 @@ public class QSTileHostTest extends SysuiTestCase { TileLifecycleManager.Factory tileLifecycleManagerFactory, UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) { super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager, - tunerService, autoTiles, shadeController, qsLogger, + tunerService, autoTiles, shadeController, qsLogger, userTracker, secureSettings, customTileStatePersister, tileLifecycleManagerFactory, userFileManager, featureFlags); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt index 2e637084e6dc..970cd17a731a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt @@ -4,7 +4,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -13,9 +12,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class QSPipelineFlagsRepositoryTest : SysuiTestCase() { - private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic() - - private val underTest = QSPipelineFlagsRepository(fakeFeatureFlagsClassic) + private val underTest = QSPipelineFlagsRepository() @Test fun pipelineFlagDisabled() { @@ -30,4 +27,18 @@ class QSPipelineFlagsRepositoryTest : SysuiTestCase() { assertThat(underTest.pipelineEnabled).isTrue() } + + @Test + fun tilesFlagDisabled() { + mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_TILES) + + assertThat(underTest.tilesEnabled).isFalse() + } + + @Test + fun tilesFlagEnabled() { + mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_TILES) + + assertThat(underTest.tilesEnabled).isTrue() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt new file mode 100644 index 000000000000..d8199c527cac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles + +import android.os.Handler +import android.service.quicksettings.Tile +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.QSHost +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.res.R +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.mockito.Mock +import org.mockito.MockitoAnnotations + +/** + * This class tests the functionality of the RecordIssueTile. The initial state of the tile is + * always be inactive at the start of these tests. + */ +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class RecordIssueTileTest : SysuiTestCase() { + + @Mock private lateinit var host: QSHost + @Mock private lateinit var qsEventLogger: QsEventLogger + @Mock private lateinit var metricsLogger: MetricsLogger + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var qsLogger: QSLogger + + private lateinit var tile: RecordIssueTile + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(host.context).thenReturn(mContext) + + val testableLooper = TestableLooper.get(this) + tile = + RecordIssueTile( + host, + qsEventLogger, + testableLooper.looper, + Handler(testableLooper.looper), + FalsingManagerFake(), + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger + ) + } + + @Test + fun qsTileUi_shouldLookCorrect_whenInactive() { + tile.isRecording = false + + val testState = tile.newTileState() + tile.handleUpdateState(testState, null) + + assertThat(testState.state).isEqualTo(Tile.STATE_INACTIVE) + assertThat(testState.secondaryLabel.toString()) + .isEqualTo(mContext.getString(R.string.qs_record_issue_start)) + } + + @Test + fun qsTileUi_shouldLookCorrect_whenRecording() { + tile.isRecording = true + + val testState = tile.newTileState() + tile.handleUpdateState(testState, null) + + assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE) + assertThat(testState.secondaryLabel.toString()) + .isEqualTo(mContext.getString(R.string.qs_record_issue_stop)) + } + + @Test + fun inActiveQsTile_switchesToActive_whenClicked() { + tile.isRecording = false + + val testState = tile.newTileState() + tile.handleUpdateState(testState, null) + + assertThat(testState.state).isEqualTo(Tile.STATE_INACTIVE) + } + + @Test + fun activeQsTile_switchesToInActive_whenClicked() { + tile.isRecording = true + + val testState = tile.newTileState() + tile.handleUpdateState(testState, null) + + assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt index 0173c32bbb94..b90ccc0e3d7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.flags.ReleasedFlag import com.android.systemui.flags.ResourceBooleanFlag import com.android.systemui.flags.UnreleasedFlag import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl +import com.android.systemui.media.controls.util.MediaInSceneContainerFlag import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -71,6 +72,7 @@ internal class SceneContainerFlagsTest( AconfigFlags.FLAG_SCENE_CONTAINER, AconfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, KeyguardShadeMigrationNssl.FLAG_NAME, + MediaInSceneContainerFlag.FLAG_NAME, ) .forEach { flagToken -> setFlagsRule.enableFlags(flagToken) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 39739e7bb93d..5ffbe65d2c50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -75,6 +75,7 @@ import com.android.systemui.scene.SceneTestUtils; import com.android.systemui.scene.data.repository.SceneContainerRepository; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.data.repository.FakeShadeRepository; @@ -138,6 +139,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private ShadeWindowLogger mShadeWindowLogger; @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private UserTracker mUserTracker; + @Mock private SceneContainerFlags mSceneContainerFlags; @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; @@ -274,7 +276,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor, - mUserTracker) { + mUserTracker, + mSceneContainerFlags) { @Override protected boolean isDebuggable() { return false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt index f8aa359b569d..750693c483a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -19,33 +19,20 @@ package com.android.systemui.shade.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.shade.ShadeExpansionChangeEvent -import com.android.systemui.shade.ShadeExpansionStateManager -import com.android.systemui.shade.domain.model.ShadeModel -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.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.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class ShadeRepositoryImplTest : SysuiTestCase() { - @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) @@ -53,57 +40,10 @@ class ShadeRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { - MockitoAnnotations.initMocks(this) - - underTest = ShadeRepositoryImpl(shadeExpansionStateManager) - `when`(shadeExpansionStateManager.addExpansionListener(any())) - .thenReturn(ShadeExpansionChangeEvent(0f, false, false, 0f)) + underTest = ShadeRepositoryImpl() } @Test - fun shadeExpansionChangeEvent() = - testScope.runTest { - var latest: ShadeModel? = null - val job = underTest.shadeModel.onEach { latest = it }.launchIn(this) - runCurrent() - assertThat(latest?.expansionAmount).isEqualTo(0f) - assertThat(latest?.isExpanded).isEqualTo(false) - assertThat(latest?.isUserDragging).isEqualTo(false) - - val captor = withArgCaptor { - verify(shadeExpansionStateManager).addExpansionListener(capture()) - } - - captor.onPanelExpansionChanged( - ShadeExpansionChangeEvent( - fraction = 1f, - expanded = true, - tracking = false, - dragDownPxAmount = 0f, - ) - ) - runCurrent() - assertThat(latest?.expansionAmount).isEqualTo(1f) - assertThat(latest?.isExpanded).isEqualTo(true) - assertThat(latest?.isUserDragging).isEqualTo(false) - - captor.onPanelExpansionChanged( - ShadeExpansionChangeEvent( - fraction = .67f, - expanded = false, - tracking = true, - dragDownPxAmount = 0f, - ) - ) - runCurrent() - assertThat(latest?.expansionAmount).isEqualTo(.67f) - assertThat(latest?.isExpanded).isEqualTo(false) - assertThat(latest?.isUserDragging).isEqualTo(true) - - job.cancel() - } - - @Test fun updateQsExpansion() = testScope.runTest { assertThat(underTest.qsExpansion.value).isEqualTo(0f) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index b98dc0016066..a3cff87e63b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -39,12 +39,15 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; +import com.android.systemui.util.settings.FakeGlobalSettings; +import com.android.systemui.util.time.SystemClock; +import com.android.systemui.util.time.SystemClockImpl; import org.junit.After; import org.junit.Before; @@ -74,18 +77,26 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { protected final Runnable mTestTimeoutRunnable = () -> mTimedOut = true; protected Handler mTestHandler; + protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings(); + protected final SystemClock mSystemClock = new SystemClockImpl(); protected boolean mTimedOut = false; @Mock protected ExpandableNotificationRow mRow; + static { + assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME); + assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME); + assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME); + } + private static class TestableAlertingNotificationManager extends AlertingNotificationManager { private AlertEntry mLastCreatedEntry; - private TestableAlertingNotificationManager(Handler handler) { - super(new HeadsUpManagerLogger(logcatLogBuffer()), handler); + private TestableAlertingNotificationManager(Handler handler, SystemClock systemClock) { + super(new HeadsUpManagerLogger(logcatLogBuffer()), handler, systemClock); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; - mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; - mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME; + mAutoDismissTime = TEST_AUTO_DISMISS_TIME; + mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME; } @Override @@ -107,7 +118,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { } protected AlertingNotificationManager createAlertingNotificationManager() { - return new TestableAlertingNotificationManager(mTestHandler); + return new TestableAlertingNotificationManager(mTestHandler, mSystemClock); } protected StatusBarNotification createSbn(int id, Notification n) { @@ -167,10 +178,6 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { @Before public void setUp() { mTestHandler = Handler.createAsync(Looper.myLooper()); - - assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME); - assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME); - assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_TIMEOUT_TIME); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 8bc5e70d09f6..34c7b09ba86a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -55,8 +55,10 @@ import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.provider.Settings; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.SparseArray; @@ -96,12 +98,26 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class NotificationLockscreenUserManagerTest extends SysuiTestCase { + + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf(FLAG_ALLOW_PRIVATE_PROFILE); + } + + public NotificationLockscreenUserManagerTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + private static final int TEST_PROFILE_USERHANDLE = 12; @Mock private NotificationPresenter mPresenter; @@ -762,8 +778,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test + @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE) public void testProfileAvailabilityIntent() { - mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE); mLockscreenUserManager.mCurrentProfiles.clear(); assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size()); mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class)); @@ -773,8 +789,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test + @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE) public void testProfileUnAvailabilityIntent() { - mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE); mLockscreenUserManager.mCurrentProfiles.clear(); assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size()); mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class)); @@ -784,8 +800,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test + @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE) public void testManagedProfileAvailabilityIntent() { - mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE); mLockscreenUserManager.mCurrentProfiles.clear(); mLockscreenUserManager.mCurrentManagedProfiles.clear(); assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size()); @@ -798,8 +814,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { } @Test + @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE) public void testManagedProfileUnAvailabilityIntent() { - mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE); mLockscreenUserManager.mCurrentProfiles.clear(); mLockscreenUserManager.mCurrentManagedProfiles.clear(); assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index fa5fad06b671..255cf6fbed63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -15,11 +15,12 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.setFlagValue import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder @@ -40,10 +41,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations.initMocks +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -66,12 +66,6 @@ class StackCoordinatorTest : SysuiTestCase() { fun setUp() { initMocks(this) entry = NotificationEntryBuilder().setSection(section).build() - setUpWithFlags() - } - - private fun setUpWithFlags(vararg flags: Pair<String, Boolean>) { - flags.forEach { (name, value) -> mSetFlagsRule.setFlagValue(name, value) } - reset(pipeline) coordinator = StackCoordinator( groupExpansionManagerImpl, @@ -86,15 +80,15 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test + @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME) fun testUpdateNotificationIcons() { - setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to false) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry))) } @Test + @EnableFlags(NotificationIconContainerRefactor.FLAG_NAME) fun testSetRenderedListOnInteractor() { - setUpWithFlags(NotificationIconContainerRefactor.FLAG_NAME to true) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) verify(renderListInteractor).setRenderedList(eq(listOf(entry))) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index 22c5bae93489..57dac3ac19a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java @@ -16,12 +16,11 @@ package com.android.systemui.statusbar.notification.footer.ui.view; +import static com.android.systemui.log.LogAssertKt.assertLogsWtf; import static com.google.common.truth.Truth.assertThat; - import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -31,33 +30,47 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; -import android.testing.AndroidTestingRunner; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import androidx.test.filters.SmallTest; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(ParameterizedAndroidJunit4.class) public class FooterViewTest extends SysuiTestCase { + @Parameters(name = "{0}") + public static List<FlagsParameterization> getFlags() { + return FlagsParameterization.allCombinationsOf(FooterViewRefactor.FLAG_NAME); + } + + public FooterViewTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + FooterView mView; Context mSpyContext = spy(mContext); @Before public void setUp() { - mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR); - mView = (FooterView) LayoutInflater.from(mSpyContext).inflate( R.layout.status_bar_notification_footer, null, false); mView.setAnimationDuration(0); @@ -114,6 +127,7 @@ public class FooterViewTest extends SysuiTestCase { } @Test + @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetClearAllButtonText_resourceOnlyFetchedOnce() { int resId = R.string.clear_all_notifications_text; mView.setClearAllButtonText(resId); @@ -132,6 +146,16 @@ public class FooterViewTest extends SysuiTestCase { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) + public void testSetClearAllButtonText_expectsFlagEnabled() { + clearInvocations(mSpyContext); + int resId = R.string.clear_all_notifications_text; + assertLogsWtf(()-> mView.setClearAllButtonText(resId)); + verify(mSpyContext, never()).getString(anyInt()); + } + + @Test + @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() { int resId = R.string.accessibility_clear_all; mView.setClearAllButtonDescription(resId); @@ -150,6 +174,16 @@ public class FooterViewTest extends SysuiTestCase { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) + public void testSetClearAllButtonDescription_expectsFlagEnabled() { + clearInvocations(mSpyContext); + int resId = R.string.accessibility_clear_all; + assertLogsWtf(()-> mView.setClearAllButtonDescription(resId)); + verify(mSpyContext, never()).getString(anyInt()); + } + + @Test + @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetMessageString_resourceOnlyFetchedOnce() { int resId = R.string.unlock_to_see_notif_text; mView.setMessageString(resId); @@ -168,6 +202,16 @@ public class FooterViewTest extends SysuiTestCase { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) + public void testSetMessageString_expectsFlagEnabled() { + clearInvocations(mSpyContext); + int resId = R.string.unlock_to_see_notif_text; + assertLogsWtf(()-> mView.setMessageString(resId)); + verify(mSpyContext, never()).getString(anyInt()); + } + + @Test + @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetMessageIcon_resourceOnlyFetchedOnce() { int resId = R.drawable.ic_friction_lock_closed; mView.setMessageIcon(resId); @@ -183,6 +227,15 @@ public class FooterViewTest extends SysuiTestCase { } @Test + @DisableFlags(FooterViewRefactor.FLAG_NAME) + public void testSetMessageIcon_expectsFlagEnabled() { + clearInvocations(mSpyContext); + int resId = R.drawable.ic_friction_lock_closed; + assertLogsWtf(()-> mView.setMessageIcon(resId)); + verify(mSpyContext, never()).getDrawable(anyInt()); + } + + @Test public void testSetFooterLabelVisible() { mView.setFooterLabelVisible(true); assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt index 0ba820f0972a..8ab13f5aa720 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase @@ -38,6 +38,7 @@ import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule @@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @SmallTest +@EnableFlags(FooterViewRefactor.FLAG_NAME) class FooterViewModelTest : SysuiTestCase() { private lateinit var footerViewModel: FooterViewModel @@ -106,8 +108,6 @@ class FooterViewModelTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR) - // The underTest in the component is Optional, because that matches the provider we // currently have for the footer view model. footerViewModel = testComponent.underTest.get() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt index 7361f6baa7b6..7ed33126a54f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.interruption +import android.platform.test.annotations.DisableFlags import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder @@ -34,11 +35,8 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) +@DisableFlags(VisualInterruptionRefactor.FLAG_NAME) class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() { - init { - mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME) - } - override val provider by lazy { NotificationInterruptStateProviderWrapper( NotificationInterruptStateProviderImpl( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt index d2c046c67fb0..da68d9c743ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.interruption +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -27,11 +28,8 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) +@EnableFlags(VisualInterruptionRefactor.FLAG_NAME) class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() { - init { - mSetFlagsRule.enableFlags(VisualInterruptionRefactor.FLAG_NAME) - } - override val provider by lazy { VisualInterruptionDecisionProviderImpl( ambientDisplayConfiguration, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt index 198f278bf43a..a07b5705d171 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent @@ -56,6 +56,7 @@ class SharedNotificationContainerInteractorTest : SysuiTestCase() { overrideResource(R.dimen.notification_panel_margin_bottom, 10) overrideResource(R.dimen.notification_panel_margin_top, 10) overrideResource(R.dimen.large_screen_shade_header_height, 0) + overrideResource(R.dimen.keyguard_split_shade_top_margin, 55) val dimens = collectLastValue(underTest.configurationBasedDimensions) @@ -70,5 +71,6 @@ class SharedNotificationContainerInteractorTest : SysuiTestCase() { assertThat(lastDimens.marginBottom).isGreaterThan(0) assertThat(lastDimens.marginTop).isGreaterThan(0) assertThat(lastDimens.marginTopLargeScreen).isEqualTo(0) + assertThat(lastDimens.keyguardSplitShadeTopMargin).isEqualTo(55) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index f00abc9c3f63..21774aa2f8d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import android.app.NotificationManager.Policy +import android.platform.test.annotations.EnableFlags import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase @@ -41,6 +41,7 @@ import com.android.systemui.runTest import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs +import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModelModule import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule import com.android.systemui.statusbar.policy.FakeConfigurationController @@ -59,6 +60,7 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) +@EnableFlags(FooterViewRefactor.FLAG_NAME) class NotificationListViewModelTest : SysuiTestCase() { @SysUISingleton @@ -104,8 +106,6 @@ class NotificationListViewModelTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - - mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index b4f7b20d12c2..f0205b3f5974 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -26,7 +26,7 @@ import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags -import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor @@ -55,7 +55,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { val kosmos = testKosmos().apply { - featureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } + fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) } } val testScope = kosmos.testScope val configurationRepository = kosmos.fakeConfigurationRepository @@ -100,6 +100,34 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { } @Test + fun validatePaddingTopInSplitShade() = + testScope.runTest { + overrideResource(R.bool.config_use_split_notification_shade, true) + overrideResource(R.dimen.large_screen_shade_header_height, 10) + overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + + val dimens by collectLastValue(underTest.configurationBasedDimensions) + + configurationRepository.onAnyConfigurationChange() + + assertThat(dimens!!.paddingTop).isEqualTo(30) + } + + @Test + fun validatePaddingTop() = + testScope.runTest { + overrideResource(R.bool.config_use_split_notification_shade, false) + overrideResource(R.dimen.large_screen_shade_header_height, 10) + overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + + val dimens by collectLastValue(underTest.configurationBasedDimensions) + + configurationRepository.onAnyConfigurationChange() + + assertThat(dimens!!.paddingTop).isEqualTo(0) + } + + @Test fun validateMarginEnd() = testScope.runTest { overrideResource(R.dimen.notification_panel_margin_horizontal, 50) @@ -226,9 +254,9 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { } @Test - fun positionOnLockscreenNotInSplitShade() = + fun boundsOnLockscreenNotInSplitShade() = testScope.runTest { - val position by collectLastValue(underTest.bounds) + val bounds by collectLastValue(underTest.bounds) // When not in split shade overrideResource(R.bool.config_use_split_notification_shade, false) @@ -242,16 +270,19 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { NotificationContainerBounds(top = 1f, bottom = 2f) ) - assertThat(position).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f)) + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f)) } @Test - fun positionOnLockscreenInSplitShade() = + fun boundsOnLockscreenInSplitShade() = testScope.runTest { - val position by collectLastValue(underTest.bounds) + val bounds by collectLastValue(underTest.bounds) // When in split shade overrideResource(R.bool.config_use_split_notification_shade, true) + overrideResource(R.dimen.large_screen_shade_header_height, 10) + overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + configurationRepository.onAnyConfigurationChange() runCurrent() @@ -263,8 +294,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) runCurrent() - // Top should be overridden to 0f - assertThat(position).isEqualTo(NotificationContainerBounds(top = 0f, bottom = 2f)) + // Top should be equal to bounds (1) + padding adjustment (30) + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 31f, bottom = 2f)) } @Test @@ -382,7 +413,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { val top = 123f val bottom = 456f keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom) - assertThat(bounds).isEqualTo(NotificationContainerBounds(top, bottom)) + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom)) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 051a4c1c05cd..6f65eb426e64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -47,7 +47,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.logging.BiometricUnlockLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; -import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -122,8 +121,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Mock private ViewRootImpl mViewRootImpl; @Mock - private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor; - @Mock private SelectedUserInteractor mSelectedUserInteractor; @Mock private BiometricUnlockInteractor mBiometricUnlockInteractor; @@ -160,7 +157,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mAuthController, mStatusBarStateController, mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper, mSystemClock, - mDeviceEntryHapticsInteractor, () -> mSelectedUserInteractor, mBiometricUnlockInteractor ); @@ -466,26 +462,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test - public void onFingerprintSuccess_requestSuccessHaptic() { - // WHEN biometric fingerprint succeeds - givenFingerprintModeUnlockCollapsing(); - mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, - true); - - // THEN always vibrate the device - verify(mDeviceEntryHapticsInteractor).vibrateSuccess(); - } - - @Test - public void onFingerprintFail_requestErrorHaptic() { - // WHEN biometric fingerprint fails - mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); - - // THEN always vibrate the device - verify(mDeviceEntryHapticsInteractor).vibrateError(); - } - - @Test public void onFingerprintDetect_showBouncer() { // WHEN fingerprint detect occurs mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 48b95d407246..37ee32204f32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -46,6 +46,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; import org.junit.After; import org.junit.Before; @@ -87,6 +89,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { KeyguardBypassController keyguardBypassController, ConfigurationController configurationController, Handler handler, + GlobalSettings globalSettings, + SystemClock systemClock, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger, JavaAdapter javaAdapter, @@ -101,13 +105,15 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { visualStabilityProvider, configurationController, handler, + globalSettings, + systemClock, accessibilityManagerWrapper, uiEventLogger, javaAdapter, shadeInteractor ); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; - mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; + mAutoDismissTime = TEST_AUTO_DISMISS_TIME; } } @@ -121,6 +127,8 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { mBypassController, mConfigurationController, mTestHandler, + mGlobalSettings, + mSystemClock, mAccessibilityManagerWrapper, mUiEventLogger, mJavaAdapter, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java index c24d9adf1dbd..b3708bad6917 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.platform.test.annotations.DisableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -49,6 +50,7 @@ import java.util.Optional; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper +@DisableFlags(NotificationIconContainerRefactor.FLAG_NAME) public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase { @Mock @@ -83,7 +85,6 @@ public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase @Before public void setup() { MockitoAnnotations.initMocks(this); - mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME); mController = new LegacyNotificationIconAreaControllerImpl( mContext, mStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index bbdc9ced57ee..1dafcc48f19f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -15,12 +15,13 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; - import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK; import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE; - import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -30,14 +31,14 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; -import android.platform.test.flag.junit.SetFlagsRule; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.Flags; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; @@ -64,6 +65,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter; +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -73,7 +75,6 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -100,10 +101,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); - @Rule - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( - SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); - @Before public void setup() { mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); @@ -114,29 +111,46 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mDependency.injectMockDependency(NotificationShadeWindowController.class); when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true); + + createPresenter(); + if (VisualInterruptionRefactor.isEnabled()) { + verifyAndCaptureSuppressors(); + } else { + verifyAndCaptureLegacySuppressor(); + } } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testInit_refactorDisabled() { - ensureRefactorDisabledState(); + assertFalse(VisualInterruptionRefactor.isEnabled()); + assertNull(mAlertsDisabledCondition); + assertNull(mVrModeCondition); + assertNull(mNeedsRedactionFilter); + assertNull(mPanelsDisabledCondition); + assertNotNull(mInterruptSuppressor); } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testInit_refactorEnabled() { - ensureRefactorEnabledState(); + assertTrue(VisualInterruptionRefactor.isEnabled()); + assertNotNull(mAlertsDisabledCondition); + assertNotNull(mVrModeCondition); + assertNotNull(mNeedsRedactionFilter); + assertNotNull(mPanelsDisabledCondition); + assertNull(mInterruptSuppressor); } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testNoSuppressHeadsUp_default_refactorDisabled() { - ensureRefactorDisabledState(); - assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry())); } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testNoSuppressHeadsUp_default_refactorEnabled() { - ensureRefactorEnabledState(); - assertFalse(mAlertsDisabledCondition.shouldSuppress()); assertFalse(mVrModeCondition.shouldSuppress()); assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry())); @@ -144,9 +158,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() { - ensureRefactorDisabledState(); - mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); @@ -156,9 +169,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() { - ensureRefactorEnabledState(); - mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); @@ -168,9 +180,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() { - ensureRefactorDisabledState(); - mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); @@ -180,9 +191,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() { - ensureRefactorEnabledState(); - mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); @@ -192,9 +202,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testPanelsDisabledConditionSuppressesPeek() { - ensureRefactorEnabledState(); - final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes(); assertTrue(types.contains(PEEK)); assertFalse(types.contains(PULSE)); @@ -202,9 +211,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() { - ensureRefactorDisabledState(); - when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); @@ -212,9 +220,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() { - ensureRefactorEnabledState(); - when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); @@ -227,9 +234,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressInterruptions_vrMode_refactorDisabled() { - ensureRefactorDisabledState(); - mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", @@ -237,9 +243,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressInterruptions_vrMode_refactorEnabled() { - ensureRefactorEnabledState(); - mStatusBarNotificationPresenter.mVrMode = true; assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress()); @@ -251,9 +256,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @DisableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() { - ensureRefactorDisabledState(); - when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", @@ -261,9 +265,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { } @Test + @EnableFlags(VisualInterruptionRefactor.FLAG_NAME) public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() { - ensureRefactorEnabledState(); - when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false); assertTrue("When alerts aren't enabled, interruptions are suppressed", @@ -349,18 +352,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mInterruptSuppressor = suppressorCaptor.getValue(); } - private void ensureRefactorDisabledState() { - mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); - createPresenter(); - verifyAndCaptureLegacySuppressor(); - } - - private void ensureRefactorEnabledState() { - mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR); - createPresenter(); - verifyAndCaptureSuppressors(); - } - private NotificationEntry createNotificationEntry() { return new NotificationEntryBuilder() .setPkg("a") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 17c29382b39a..1cc611c2df87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -69,8 +69,8 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.ui.SystemBarUtilsState; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -717,7 +717,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mKeyguardUpdateMonitor, mock(NotificationIconContainerStatusBarViewModel.class), mock(ConfigurationState.class), - mock(ConfigurationController.class), + mock(SystemBarUtilsState.class), mock(StatusBarNotificationIconViewStore.class), mock(DemoModeController.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index 09dc1e537e9f..89842d6274e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -16,9 +16,10 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest import com.android.systemui.CoroutineTestScopeModule -import com.android.systemui.Flags import com.android.systemui.SysUITestComponent import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase @@ -29,6 +30,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepos import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.log.assertLogsWtf import com.android.systemui.runTest import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository @@ -37,14 +39,15 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.google.common.truth.Truth.assertThat import dagger.BindsInstance import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher -import org.junit.Before import org.junit.Test @SmallTest @@ -79,11 +82,6 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { testScope = CoroutineTestScopeModule(TestScope(UnconfinedTestDispatcher())), ) - @Before - fun setUp() { - mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR) - } - @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = testComponent.runTest { @@ -347,6 +345,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { } @Test + @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_lowProfileWithNotifications_true() = testComponent.runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = @@ -360,6 +359,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { } @Test + @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() = testComponent.runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = @@ -373,6 +373,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { } @Test + @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() = testComponent.runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT @@ -385,6 +386,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { } @Test + @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() = testComponent.runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT @@ -396,6 +398,16 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(actual).isFalse() } + @Test + @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME) + fun areNotificationsLightsOut_requiresFlagEnabled() = + testComponent.runTest { + assertLogsWtf { + val flow = underTest.areNotificationsLightsOut(DISPLAY_ID) + assertThat(flow).isEqualTo(emptyFlow<Boolean>()) + } + } + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt index 22dce3a2ff64..1bdf64434fcb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt @@ -21,6 +21,7 @@ import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVIC import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription import com.android.systemui.common.shared.model.Text +import com.android.systemui.common.shared.model.Text.Companion.loadText import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags @@ -355,14 +356,14 @@ class InternetTileViewModelTest : SysuiTestCase() { connectivityRepository.setEthernetConnected(default = true, validated = true) - assertThat(latest?.secondaryLabel).isNull() - assertThat(latest?.secondaryTitle) - .isEqualTo(ethernetIcon!!.contentDescription.toString()) + assertThat(latest?.secondaryLabel.loadText(context)) + .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context)) + assertThat(latest?.secondaryTitle).isNull() assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully) assertThat(latest?.icon).isNull() assertThat(latest?.stateDescription).isNull() assertThat(latest?.contentDescription.loadContentDescription(context)) - .isEqualTo(latest?.secondaryTitle) + .isEqualTo(latest?.secondaryLabel.loadText(context)) } @Test @@ -373,14 +374,14 @@ class InternetTileViewModelTest : SysuiTestCase() { connectivityRepository.setEthernetConnected(default = true, validated = false) - assertThat(latest?.secondaryLabel).isNull() - assertThat(latest?.secondaryTitle) - .isEqualTo(ethernetIcon!!.contentDescription.toString()) + assertThat(latest?.secondaryLabel.loadText(context)) + .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context)) + assertThat(latest?.secondaryTitle).isNull() assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet) assertThat(latest?.icon).isNull() assertThat(latest?.stateDescription).isNull() assertThat(latest?.contentDescription.loadContentDescription(context)) - .isEqualTo(latest?.secondaryTitle) + .isEqualTo(latest?.secondaryLabel.loadText(context)) } private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 4f3f56423eb0..2940c398c315 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -34,7 +34,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; import android.app.Notification; import android.app.PendingIntent; @@ -57,17 +56,25 @@ import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class HeadsUpManagerTest extends AlertingNotificationManagerTest { +public class BaseHeadsUpManagerTest extends AlertingNotificationManagerTest { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + private static final int TEST_TOUCH_ACCEPTANCE_TIME = 200; private static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000; private static final int TEST_A11Y_TIMEOUT_TIME = 3_000; @@ -76,17 +83,33 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer())); @Mock private AccessibilityManagerWrapper mAccessibilityMgr; + static { + assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME); + assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME); + assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME); + + assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan( + TEST_TIMEOUT_TIME); + assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan( + TEST_TIMEOUT_TIME); + assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan( + TEST_A11Y_TIMEOUT_TIME); + } + private final class TestableHeadsUpManager extends BaseHeadsUpManager { TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler, + GlobalSettings globalSettings, + SystemClock systemClock, AccessibilityManagerWrapper accessibilityManagerWrapper, UiEventLogger uiEventLogger) { - super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger); + super(context, logger, handler, globalSettings, systemClock, + accessibilityManagerWrapper, uiEventLogger); mTouchAcceptanceDelay = TEST_TOUCH_ACCEPTANCE_TIME; mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; - mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; - mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME; + mAutoDismissTime = TEST_AUTO_DISMISS_TIME; + mStickyForSomeTimeAutoDismissTime = TEST_STICKY_AUTO_DISMISS_TIME; } // The following are only implemented by HeadsUpManagerPhone. If you need them, use that. @@ -160,8 +183,8 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { } private BaseHeadsUpManager createHeadsUpManager() { - return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr, - mUiEventLoggerFake); + return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mGlobalSettings, + mSystemClock, mAccessibilityMgr, mUiEventLoggerFake); } @Override @@ -214,19 +237,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { @Before @Override public void setUp() { - initMocks(this); super.setUp(); - - assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME); - assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME); - assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME); - - assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_AUTO_DISMISS_TIME).isLessThan( - TEST_TIMEOUT_TIME); - assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_STICKY_AUTO_DISMISS_TIME).isLessThan( - TEST_TIMEOUT_TIME); - assertThat(TEST_TOUCH_ACCEPTANCE_TIME + TEST_A11Y_AUTO_DISMISS_TIME).isLessThan( - TEST_A11Y_TIMEOUT_TIME); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index 01dad381efa0..479309c18c92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -153,6 +153,36 @@ public class KeyguardStateControllerTest extends SysuiTestCase { } @Test + public void testCanSkipLockScreen_updateCalledOnFacesCleared() { + verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture()); + + // Cannot skip after there's a password/pin/pattern + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */); + assertThat(mKeyguardStateController.canDismissLockScreen()).isFalse(); + + // Unless user is authenticated + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true); + mUpdateCallbackCaptor.getValue().onFacesCleared(); + assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue(); + } + + @Test + public void testCanSkipLockScreen_updateCalledOnFingerprintssCleared() { + verify(mKeyguardUpdateMonitor).registerCallback(mUpdateCallbackCaptor.capture()); + + // Cannot skip after there's a password/pin/pattern + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */); + assertThat(mKeyguardStateController.canDismissLockScreen()).isFalse(); + + // Unless user is authenticated + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true); + mUpdateCallbackCaptor.getValue().onFingerprintsCleared(); + assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue(); + } + + @Test public void testIsUnlocked() { // Is unlocked whenever the keyguard is not showing assertThat(mKeyguardStateController.isShowing()).isFalse(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 8585d46fa8a5..5b9b390eea2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -119,6 +119,7 @@ import com.android.systemui.scene.SceneTestUtils; import com.android.systemui.scene.data.repository.SceneContainerRepository; import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; +import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.scene.shared.logger.SceneLogger; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; @@ -343,6 +344,8 @@ public class BubblesTest extends SysuiTestCase { private Icon mAppBubbleIcon; @Mock private Display mDefaultDisplay; + @Mock + private SceneContainerFlags mSceneContainerFlags; private final SceneTestUtils mUtils = new SceneTestUtils(this); private final TestScope mTestScope = mUtils.getTestScope(); @@ -503,7 +506,8 @@ public class BubblesTest extends SysuiTestCase { () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor, - mUserTracker + mUserTracker, + mSceneContainerFlags ); mNotificationShadeWindowController.fetchWindowRootView(); mNotificationShadeWindowController.attach(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt index 8bb07d9487cd..94fc1fce7f6f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/android/content/pm/LauncherAppsKosmos.kt @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.systemui.deviceentry.data.repository +package android.content.pm import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock -var Kosmos.deviceEntryHapticsRepository: DeviceEntryHapticsRepository by - Kosmos.Fixture { fakeDeviceEntryHapticsRepository } -val Kosmos.fakeDeviceEntryHapticsRepository by Kosmos.Fixture { FakeDeviceEntryHapticsRepository() } +val Kosmos.launcherApps by Kosmos.Fixture { mock<LauncherApps>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index 4fdea9713ab0..7c5696c716d8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -20,10 +20,10 @@ import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel.SecurityMode +import com.android.systemui.authentication.shared.model.AuthenticationLockoutModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate import com.android.systemui.authentication.shared.model.AuthenticationResultModel -import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.SysUISingleton import dagger.Binds import dagger.Module @@ -40,9 +40,6 @@ class FakeAuthenticationRepository( private val currentTime: () -> Long, ) : AuthenticationRepository { - private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false) - override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> = - _isAutoConfirmFeatureEnabled.asStateFlow() override val authenticationChallengeResult = MutableSharedFlow<Boolean>() override val hintedPinLength: Int = HINTING_PIN_LENGTH @@ -50,8 +47,13 @@ class FakeAuthenticationRepository( private val _isPatternVisible = MutableStateFlow(true) override val isPatternVisible: StateFlow<Boolean> = _isPatternVisible.asStateFlow() - override val throttling: MutableStateFlow<AuthenticationThrottlingModel?> = - MutableStateFlow(null) + override val lockout: MutableStateFlow<AuthenticationLockoutModel?> = MutableStateFlow(null) + + override val hasLockoutOccurred = MutableStateFlow(false) + + private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false) + override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> = + _isAutoConfirmFeatureEnabled.asStateFlow() private val _authenticationMethod = MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD) @@ -67,10 +69,12 @@ class FakeAuthenticationRepository( _isPinEnhancedPrivacyEnabled.asStateFlow() private var failedAttemptCount = 0 - private var throttlingEndTimestamp = 0L + private var lockoutEndTimestamp = 0L private var credentialOverride: List<Any>? = null private var securityMode: SecurityMode = DEFAULT_AUTHENTICATION_METHOD.toSecurityMode() + var lockoutStartedReportCount = 0 + override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return authenticationMethod.value } @@ -89,6 +93,10 @@ class FakeAuthenticationRepository( authenticationChallengeResult.emit(isSuccessful) } + override suspend fun reportLockoutStarted(durationMs: Int) { + lockoutStartedReportCount++ + } + override suspend fun getPinLength(): Int { return (credentialOverride ?: DEFAULT_PIN).size } @@ -97,16 +105,19 @@ class FakeAuthenticationRepository( return failedAttemptCount } - override suspend fun getThrottlingEndTimestamp(): Long { - return throttlingEndTimestamp + override suspend fun getLockoutEndTimestamp(): Long { + return lockoutEndTimestamp } fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) { _isAutoConfirmFeatureEnabled.value = isEnabled } - override suspend fun setThrottleDuration(durationMs: Int) { - throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0 + override suspend fun setLockoutDuration(durationMs: Int) { + lockoutEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0 + if (durationMs > 0) { + hasLockoutOccurred.value = true + } } override suspend fun checkCredential( @@ -125,17 +136,16 @@ class FakeAuthenticationRepository( else -> error("Unexpected credential type ${credential.type}!") } - return if ( - isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1 - ) { + return if (isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { + hasLockoutOccurred.value = false AuthenticationResultModel( isSuccessful = isSuccessful, - throttleDurationMs = 0, + lockoutDurationMs = 0, ) } else { AuthenticationResultModel( isSuccessful = false, - throttleDurationMs = THROTTLE_DURATION_MS, + lockoutDurationMs = LOCKOUT_DURATION_MS, ) } } @@ -165,8 +175,9 @@ class FakeAuthenticationRepository( AuthenticationPatternCoordinate(0, 1), AuthenticationPatternCoordinate(0, 2), ) - const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5 - const val THROTTLE_DURATION_MS = 30000 + const val MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT = 5 + const val LOCKOUT_DURATION_SECONDS = 30 + const val LOCKOUT_DURATION_MS = LOCKOUT_DURATION_SECONDS * 1000 const val HINTING_PIN_LENGTH = 6 val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt index d97cb56787cc..8ff04a63802a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt @@ -16,7 +16,6 @@ package com.android.systemui.deviceentry.data import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule -import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryHapticsRepositoryModule import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule @@ -29,7 +28,6 @@ import dagger.Module [ FakeBiometricSettingsRepositoryModule::class, FakeDeviceEntryRepositoryModule::class, - FakeDeviceEntryHapticsRepositoryModule::class, FakeDeviceEntryFaceAuthRepositoryModule::class, FakeDeviceEntryFingerprintAuthRepositoryModule::class, FakeFingerprintPropertyRepositoryModule::class, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt deleted file mode 100644 index b29b67d7c220..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryHapticsRepository.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.deviceentry.data.repository - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** Fake implementation of [DeviceEntryHapticsRepository] */ -@SysUISingleton -class FakeDeviceEntryHapticsRepository @Inject constructor() : DeviceEntryHapticsRepository { - private var _successHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow() - - private var _errorHapticRequest: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow() - - override fun requestSuccessHaptic() { - _successHapticRequest.value = true - } - - override fun handleSuccessHaptic() { - _successHapticRequest.value = false - } - - override fun requestErrorHaptic() { - _errorHapticRequest.value = true - } - - override fun handleErrorHaptic() { - _errorHapticRequest.value = false - } -} - -@Module -interface FakeDeviceEntryHapticsRepositoryModule { - @Binds fun bindFake(fake: FakeDeviceEntryHapticsRepository): DeviceEntryHapticsRepository -} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index ba70d46fd954..6436a382eb7f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -16,16 +16,24 @@ package com.android.systemui.deviceentry.data.repository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import dagger.Binds import dagger.Module import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow /** Fake implementation of [DeviceEntryRepository] */ @SysUISingleton class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { + private val _enteringDeviceFromBiometricUnlock: MutableSharedFlow<BiometricUnlockSource> = + MutableSharedFlow() + override val enteringDeviceFromBiometricUnlock: Flow<BiometricUnlockSource> = + _enteringDeviceFromBiometricUnlock.asSharedFlow() private var isLockscreenEnabled = true @@ -54,6 +62,10 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { fun setBypassEnabled(isBypassEnabled: Boolean) { _isBypassEnabled.value = isBypassEnabled } + + suspend fun enteringDeviceFromBiometricUnlock(sourceType: BiometricUnlockSource) { + _enteringDeviceFromBiometricUnlock.emit(sourceType) + } } @Module diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt new file mode 100644 index 000000000000..1bd105620813 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.biometricSettingsRepository +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryBiometricAuthInteractor by + Kosmos.Fixture { + DeviceEntryBiometricAuthInteractor( + biometricSettingsRepository = biometricSettingsRepository, + deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt new file mode 100644 index 000000000000..d2dff7834b40 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryFaceAuthInteractor by + Kosmos.Fixture { + DeviceEntryFaceAuthInteractor( + repository = deviceEntryFaceAuthRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt new file mode 100644 index 000000000000..66c6f86c452d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractorKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository +import com.android.systemui.kosmos.Kosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryFingerprintAuthInteractor by + Kosmos.Fixture { + DeviceEntryFingerprintAuthInteractor( + repository = deviceEntryFingerprintAuthRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt index de6cacb00faa..6bf527df4026 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt @@ -20,7 +20,6 @@ package com.android.systemui.deviceentry.domain.interactor import com.android.keyguard.logging.biometricUnlockLogger import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository -import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryHapticsRepository import com.android.systemui.keyevent.domain.interactor.keyEventInteractor import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.kosmos.Kosmos @@ -31,7 +30,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.deviceEntryHapticsInteractor by Kosmos.Fixture { DeviceEntryHapticsInteractor( - repository = fakeDeviceEntryHapticsRepository, + deviceEntryInteractor = deviceEntryInteractor, + deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor, + deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor, fingerprintPropertyRepository = fingerprintPropertyRepository, biometricSettingsRepository = biometricSettingsRepository, keyEventInteractor = keyEventInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt index e6b7f62c7d5f..abadaf754c30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt @@ -16,6 +16,45 @@ package com.android.systemui.flags +import android.content.res.mainResources import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock -val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() } +/** + * Main fixture for supplying a [FeatureFlagsClassic]. Should be used by other fixtures. Unless + * overridden in the test, this by default uses [fakeFeatureFlagsClassic]. + */ +var Kosmos.featureFlagsClassic: FeatureFlagsClassic by Kosmos.Fixture { fakeFeatureFlagsClassic } + +/** + * Fixture supplying a shared [FakeFeatureFlagsClassic] instance. Can be accessed in tests in order + * to override flag values. + */ +val Kosmos.fakeFeatureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() } + +/** + * Fixture supplying a real [FeatureFlagsClassicRelease] instance, for use by tests that want to + * reflect the current state of the device in release builds (example: screenshot tests). + * + * By default, this fixture is unused; tests should override [featureFlagsClassic] in order to + * utilize this fixture: + * ```kotlin + * val kosmos = Kosmos() + * kosmos.featureFlagsClassic = kosmos.featureFlagsClassicRelease + * ``` + */ +val Kosmos.featureFlagsClassicRelease by + Kosmos.Fixture { + FeatureFlagsClassicRelease( + /* resources = */ mainResources, + /* systemProperties = */ systemPropertiesHelper, + /* serverFlagReader = */ serverFlagReader, + /* allFlags = */ FlagsCommonModule.providesAllFlags(), + /* restarter = */ restarter, + ) + } + +val Kosmos.systemPropertiesHelper by Kosmos.Fixture { SystemPropertiesHelper() } +var Kosmos.serverFlagReader: ServerFlagReader by Kosmos.Fixture { serverFlagReaderFake } +val Kosmos.serverFlagReaderFake by Kosmos.Fixture { ServerFlagReaderFake() } +var Kosmos.restarter: Restarter by Kosmos.Fixture { mock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt index e289083a2d9e..a1b6587be0b8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.filterNotNull @SysUISingleton class FakeDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository { - override val isAuthenticated = MutableStateFlow(false) override val canRunFaceAuth = MutableStateFlow(false) private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt index 299262b91027..6557bcfd8337 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.burnInInteractor @@ -45,7 +44,6 @@ val Kosmos.deviceEntryIconViewModel by Fixture { shadeDependentFlows = shadeDependentFlows, sceneContainerFlags = sceneContainerFlags, keyguardViewController = { statusBarKeyguardViewManager }, - deviceEntryHapticsInteractor = deviceEntryHapticsInteractor, deviceEntryInteractor = deviceEntryInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt new file mode 100644 index 000000000000..10f9346aba14 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log + +import android.util.Log +import android.util.Log.TerribleFailureHandler +import junit.framework.Assert + +/** + * Assert that the given block makes a call to Log.wtf + * + * @return the details of the log + */ +fun assertLogsWtf( + message: String = "Expected Log.wtf to be called", + allowMultiple: Boolean = false, + loggingBlock: () -> Unit, +): TerribleFailureLog { + var caught: TerribleFailureLog? = null + var count = 0 + val newHandler = TerribleFailureHandler { tag, failure, system -> + if (caught == null) { + caught = TerribleFailureLog(tag, failure, system) + } + count++ + } + val oldHandler = Log.setWtfHandler(newHandler) + try { + loggingBlock() + } finally { + Log.setWtfHandler(oldHandler) + } + Assert.assertNotNull(message, caught) + if (!allowMultiple && count != 1) { + Assert.fail("Unexpectedly caught Log.Wtf $count times; expected only 1. First: $caught") + } + return caught!! +} + +@JvmOverloads +fun assertLogsWtf( + message: String = "Expected Log.wtf to be called", + allowMultiple: Boolean = false, + loggingRunnable: Runnable, +): TerribleFailureLog = + assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() } + +/** The data passed to [TerribleFailureHandler.onTerribleFailure] */ +data class TerribleFailureLog( + val tag: String, + val failure: Log.TerribleFailure, + val system: Boolean +) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt new file mode 100644 index 000000000000..e9a394aef6de --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * 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.tiles.impl.saver + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger +import com.android.systemui.statusbar.connectivity.ConnectivityModule + +val Kosmos.qsDataSaverTileConfig by + Kosmos.Fixture { ConnectivityModule.provideDataSaverTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt index 9c108487e4c5..f7005ab45ea0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt @@ -18,21 +18,14 @@ package com.android.systemui.shade.data.repository import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.shade.domain.model.ShadeModel import dagger.Binds import dagger.Module import javax.inject.Inject -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow /** Fake implementation of [ShadeRepository] */ @SysUISingleton class FakeShadeRepository @Inject constructor() : ShadeRepository { - - private val _shadeModel = MutableStateFlow(ShadeModel()) - override val shadeModel: Flow<ShadeModel> = _shadeModel - private val _qsExpansion = MutableStateFlow(0f) override val qsExpansion = _qsExpansion @@ -114,10 +107,6 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository { _legacyIsClosing.value = isClosing } - fun setShadeModel(model: ShadeModel) { - _shadeModel.value = model - } - override fun setQsExpansion(qsExpansion: Float) { _qsExpansion.value = qsExpansion } diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values-sw600dp/config.xml b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt index be1f081d5b8f..a48b27015c02 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values-sw600dp/config.xml +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotifPipelineKosmos.kt @@ -1,13 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright (c) 2023, The Android Open Source Project +/* + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<resources> - <!-- If true, attach the navigation bar to the app during app transition --> - <bool name="config_attachNavBarToAppDuringTransition">false</bool> -</resources> + +package com.android.systemui.statusbar.notification.collection + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.notifPipeline by Kosmos.Fixture { mock<NotifPipeline>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt new file mode 100644 index 000000000000..f00538e3fbcc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollectionKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.notifcollection + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.collection.notifPipeline + +var Kosmos.commonNotifCollection by Kosmos.Fixture { notifPipeline } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt new file mode 100644 index 000000000000..e7c4085cabfd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProviderKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.provider + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.sectionStyleProvider: SectionStyleProvider by Kosmos.Fixture { mock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt new file mode 100644 index 000000000000..f7acae9846df --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractorKosmos.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.collection.provider.sectionStyleProvider +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository + +val Kosmos.renderNotificationListInteractor by + Kosmos.Fixture { + RenderNotificationListInteractor(activeNotificationListRepository, sectionStyleProvider) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt new file mode 100644 index 000000000000..4535652eb8c5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconBuilderKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.icon + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.iconBuilder by Kosmos.Fixture { IconBuilder(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt new file mode 100644 index 000000000000..d3a8e0c5970c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/IconManagerKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.icon + +import android.content.pm.launcherApps +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.collection.notifcollection.commonNotifCollection + +val Kosmos.iconManager by + Kosmos.Fixture { IconManager(commonNotifCollection, launcherApps, iconBuilder) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt index 75e5aeafd52d..ca5b4010fd7d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt @@ -26,18 +26,18 @@ import com.android.systemui.statusbar.notification.icon.ui.viewbinder.shelfNotif import com.android.systemui.statusbar.notification.icon.ui.viewbinder.statusBarIconViewBindingFailureTracker import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel import com.android.systemui.statusbar.phone.notificationIconAreaController -import com.android.systemui.statusbar.policy.configurationController +import com.android.systemui.statusbar.ui.systemBarUtilsState val Kosmos.notificationListViewBinder by Fixture { NotificationListViewBinder( viewModel = notificationListViewModel, backgroundDispatcher = testDispatcher, configuration = configurationState, - configurationController = configurationController, falsingManager = falsingManager, iconAreaController = notificationIconAreaController, iconViewBindingFailureTracker = statusBarIconViewBindingFailureTracker, metricsLogger = metricsLogger, shelfIconViewStore = shelfNotificationIconViewStore, + systemBarUtilsState = systemBarUtilsState, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt new file mode 100644 index 000000000000..d38baba8876f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/FakeSystemBarUtilsProxy.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.ui + +class FakeSystemBarUtilsProxy(private var statusBarHeight: Int) : SystemBarUtilsProxy { + override fun getStatusBarHeight(): Int = statusBarHeight +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt new file mode 100644 index 000000000000..f24037d4aea2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsProxyKosmos.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.ui + +import android.content.applicationContext +import android.content.res.mainResources +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.res.R + +/** + * Main fixture for supplying a [SystemBarUtilsProxy]. Should be used by other fixtures. Unless + * overridden in the test, this by default uses [fakeSystemBarUtilsProxy]. + */ +var Kosmos.systemBarUtilsProxy: SystemBarUtilsProxy by Fixture { fakeSystemBarUtilsProxy } + +/** + * Fixture supplying a real [SystemBarUtilsProxyImpl] instance, for use by tests that want to use + * the real device logic to determine system bar properties. Note this this real instance does *not* + * support Robolectric tests; by opting in, you are explicitly opting-out of using Robolectric. + * + * By default, this fixture is unused; tests should override [systemBarUtilsProxy] in order to + * utilize this fixture: + * ```kotlin + * val kosmos = Kosmos() + * kosmos.systemBarUtilsProxy = kosmos.systemBarUtilsProxyImpl + * ``` + */ +val Kosmos.systemBarUtilsProxyImpl by Fixture { SystemBarUtilsProxyImpl(applicationContext) } + +/** + * Fixture supplying a shared [FakeSystemBarUtilsProxy] instance. Can be accessed or overridden in + * tests in order to provide custom results. + */ +var Kosmos.fakeSystemBarUtilsProxy by Fixture { + FakeSystemBarUtilsProxy(mainResources.getDimensionPixelSize(R.dimen.status_bar_height)) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt new file mode 100644 index 000000000000..e208add32efa --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/ui/SystemBarUtilsStateKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.ui + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.configurationController + +val Kosmos.systemBarUtilsState by + Kosmos.Fixture { SystemBarUtilsState(configurationController, systemBarUtilsProxy) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java index 886722e46376..bade84890f41 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java @@ -19,19 +19,38 @@ import android.testing.LeakCheck; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController.Listener; +import java.util.ArrayList; +import java.util.List; + public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController { + private boolean mIsEnabled = false; + private List<Listener> mListeners = new ArrayList<>(); + public FakeDataSaverController(LeakCheck test) { super(test, "datasaver"); } @Override public boolean isDataSaverEnabled() { - return false; + return mIsEnabled; } @Override public void setDataSaverEnabled(boolean enabled) { + mIsEnabled = enabled; + for (Listener listener: mListeners) { + listener.onDataSaverChanged(enabled); + } + } + @Override + public void addCallback(Listener listener) { + mListeners.add(listener); + } + + @Override + public void removeCallback(Listener listener) { + mListeners.remove(listener); } } diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt index 6a6ae3876f6b..f5e4af50fc29 100644 --- a/ravenwood/framework-minus-apex-ravenwood-policies.txt +++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt @@ -3,6 +3,9 @@ # Keep all AIDL interfaces class :aidl stubclass +# Keep all feature flag implementations +class :feature_flags stubclass + # Collections class android.util.ArrayMap stubclass class android.util.ArraySet stubclass @@ -74,6 +77,7 @@ class android.util.proto.ProtoUtils stubclass class android.util.proto.WireTypeMismatchException stubclass # Misc +class android.util.BackupUtils stubclass class android.util.Dumpable stubclass class android.util.DebugUtils stubclass class android.util.MathUtils stubclass @@ -81,15 +85,11 @@ class android.util.Patterns stubclass class android.util.UtilConfig stubclass # Internals -class com.android.internal.util.FastMath stubclass -class com.android.internal.util.FastPrintWriter stubclass -class com.android.internal.util.GrowingArrayUtils stubclass -class com.android.internal.util.LineBreakBufferedWriter stubclass -class com.android.internal.util.Parcelling stubclass +class com.android.internal.util.FileRotator stubclass +class com.android.internal.util.HexDump stubclass +class com.android.internal.util.MessageUtils stubclass class com.android.internal.util.Preconditions stubclass -class com.android.internal.util.StringPool stubclass - -class com.android.internal.os.SomeArgs stubclass +class com.android.internal.util.TokenBucket stubclass # Parcel class android.os.ParcelFormatException stubclass diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java index edb0442e7b29..916dd5943059 100644 --- a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java +++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java @@ -36,7 +36,7 @@ import java.lang.annotation.Target; * * @hide */ -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface IgnoreUnderRavenwood { /** diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 9db5b9895749..d175713eb92f 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -93,7 +93,7 @@ public class RavenwoodRule implements TestRule { /** * Return if the current process is running under a Ravenwood test environment. */ - public boolean isUnderRavenwood() { + public static boolean isUnderRavenwood() { return IS_UNDER_RAVENWOOD; } @@ -102,6 +102,9 @@ public class RavenwoodRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { + Assume.assumeFalse(IS_UNDER_RAVENWOOD); + } if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { Assume.assumeFalse(IS_UNDER_RAVENWOOD); } diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index 468b7c9188ab..2ba5f6b05e26 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -1,17 +1,30 @@ # Only classes listed here can use the Ravenwood annotations. com.android.internal.util.ArrayUtils +com.android.internal.os.LongArrayMultiStateCounter +com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer android.util.AtomicFile android.util.DataUnit +android.util.DayOfMonthCursor +android.util.DumpableContainer android.util.EventLog android.util.IntArray +android.util.KeyValueListParser android.util.LongArray +android.util.LongArrayQueue +android.util.LongSparseLongArray android.util.LruCache +android.util.MonthDisplayHelper +android.util.RecurrenceRule +android.util.RotationUtils android.util.Slog +android.util.SparseDoubleArray +android.util.SparseSetArray android.util.TimeUtils android.util.Xml +android.os.BatteryConsumer android.os.Binder android.os.Binder$IdentitySupplier android.os.FileUtils @@ -78,4 +91,33 @@ android.graphics.PointF android.graphics.Rect android.graphics.RectF +android.content.ContentProvider + com.android.server.LocalServices + +com.android.internal.os.SomeArgs + +com.android.internal.util.BitUtils +com.android.internal.util.BitwiseInputStream +com.android.internal.util.BitwiseOutputStream +com.android.internal.util.CallbackRegistry +com.android.internal.util.DumpableContainer +com.android.internal.util.dump.DumpableContainerImpl +com.android.internal.util.DumpUtils +com.android.internal.util.FastMath +com.android.internal.util.FastPrintWriter +com.android.internal.util.FileRotator +com.android.internal.util.GrowingArrayUtils +com.android.internal.util.HeavyHitterSketch +com.android.internal.util.LineBreakBufferedWriter +com.android.internal.util.ObjectUtils +com.android.internal.util.Parcelling +com.android.internal.util.ParseUtils +com.android.internal.util.ProcFileReader +com.android.internal.util.QuickSelect +com.android.internal.util.RingBuffer +com.android.internal.util.StringPool + +com.google.android.collect.Lists +com.google.android.collect.Maps +com.google.android.collect.Sets diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md index 5adef534a2b2..de05777f01cd 100644 --- a/ravenwood/test-authors.md +++ b/ravenwood/test-authors.md @@ -71,10 +71,10 @@ public class MyCodeTest { Once you’ve defined your test, you can use typical commands to execute it locally: ``` -$ atest MyTestsRavenwood +$ atest --host MyTestsRavenwood ``` -> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. +> **Note:** There's a known bug where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until bug #312525698 is fixed. You can also run your new tests automatically via `TEST_MAPPING` rules like this: @@ -89,6 +89,27 @@ You can also run your new tests automatically via `TEST_MAPPING` rules like this } ``` +## Strategies for feature flags + +Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team: + +``` +import android.platform.test.flag.junit.SetFlagsRule; + +@RunWith(AndroidJUnit4.class) +public class MyCodeTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Test + public void testEnabled() { + mSetFlagsRule.enableFlags(Flags.FLAG_MY_FLAG); + // verify test logic that depends on flag being enabled + } +``` + +This naturally composes together well with any `RavenwoodRule` that your test may need. + ## Strategies for migration/bivalent tests Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can run on both a real Android device and under a Ravenwood environment. diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index e9bb763e143a..b8cf13b11534 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -32,6 +32,22 @@ java_library_static { ], } +java_library_static { + name: "AccessibilityGestureUtils", + srcs: [ + "java/**/gestures/GestureMatcher.java", + "java/**/gestures/GestureManifold.java", + "java/**/gestures/MultiFingerMultiTap.java", + "java/**/gestures/TouchState.java", + ], + static_libs: [ + "services.accessibility", + ], + libs: [ + "androidx.annotation_annotation", + ], +} + aconfig_declarations { name: "com_android_server_accessibility_flags", package: "com.android.server.accessibility", diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 71878954e9ec..0696807b3c8c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1848,8 +1848,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public void notifyGesture(AccessibilityGestureEvent gestureEvent) { - mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, - gestureEvent).sendToTarget(); + if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { + // We will use this event async, so copy it because it contains MotionEvents. + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, + gestureEvent.copyForAsync()).sendToTarget(); + } else { + mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, + gestureEvent).sendToTarget(); + } } public void notifySystemActionsChangedLocked() { @@ -2323,9 +2329,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int type = message.what; switch (type) { case MSG_ON_GESTURE: { - notifyGestureInternal((AccessibilityGestureEvent) message.obj); + if (message.obj instanceof AccessibilityGestureEvent gesture) { + notifyGestureInternal(gesture); + if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { + gesture.recycle(); + } + } } break; - case MSG_CLEAR_ACCESSIBILITY_CACHE: { notifyClearAccessibilityCacheInternal(); } break; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index 903a07140eae..e54f0c12c0ca 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -82,7 +82,7 @@ import java.util.List; * detector. Gesture matchers are tied to a single gesture. It calls listener callback functions * when a gesture starts or completes. */ -class GestureManifold implements GestureMatcher.StateChangeListener { +public class GestureManifold implements GestureMatcher.StateChangeListener { private static final String LOG_TAG = "GestureManifold"; @@ -111,7 +111,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener { // Shared state information. private TouchState mState; - GestureManifold(Context context, Listener listener, TouchState state, Handler handler) { + public GestureManifold(Context context, Listener listener, TouchState state, Handler handler) { mContext = context; mHandler = handler; mListener = listener; @@ -222,7 +222,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener { * @return True if the event has been appropriately handled by the gesture manifold and related * callback functions, false if it should be handled further by the calling function. */ - boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mState.isClear()) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { // Validity safeguard: if touch state is clear, then matchers should always be clear diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java index 6e2fc69f2942..3668eefe293d 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java @@ -328,13 +328,21 @@ public abstract class GestureMatcher { + getStateSymbolicName(mTargetState)); } mHandler.removeCallbacks(this); + recycleEvent(); } public void post( int state, long delay, MotionEvent event, MotionEvent rawEvent, int policyFlags) { + // Recycle the old event first if necessary, to handle duplicate calls to post. + recycleEvent(); mTargetState = state; - mEvent = event; - mRawEvent = rawEvent; + if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { + mEvent = event.copy(); + mRawEvent = rawEvent.copy(); + } else { + mEvent = event; + mRawEvent = rawEvent; + } mPolicyFlags = policyFlags; mHandler.postDelayed(this, delay); if (DEBUG) { @@ -367,6 +375,19 @@ public abstract class GestureMatcher { + getStateSymbolicName(mTargetState)); } setState(mTargetState, mEvent, mRawEvent, mPolicyFlags); + recycleEvent(); + } + + private void recycleEvent() { + if (android.view.accessibility.Flags.copyEventsForGestureDetection()) { + if (mEvent == null || mRawEvent == null) { + return; + } + mEvent.recycle(); + mRawEvent.recycle(); + mEvent = null; + mRawEvent = null; + } } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index f55ecb05c55f..0a2a780adf45 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -981,17 +981,22 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH transitionToDelegatingStateAndClear(); } transitToSinglePanningStateAndClear(); - } else { + } else if (!mIsTwoFingerCountReached) { + // If it is a two-finger gesture, do not transition to the + // delegating state to ensure the reachability of + // the two-finger triple tap (triggerable with ACTION_UP) transitionToDelegatingStateAndClear(); } } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation) && distanceClosestPointerToPoint( - mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance - // If mCompleteTapCount is not zero, it means that it is a multi tap - // gesture. So, we should not transit to the PanningScalingState. - && mCompletedTapCount == 0) { + mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) { // Second pointer is swiping, so transit to PanningScalingState - transitToPanningScalingStateAndClear(); + // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from + // multi finger multi tap + storePointerDownLocation(mSecondPointerDownLocation, event); + mHandler.sendEmptyMessageDelayed( + MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE, + ViewConfiguration.getTapTimeout()); } } break; diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index c111ec3213d9..1f89e57b90d7 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -253,13 +253,8 @@ class InputController { mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); } - void setLocalIme(int displayId) { - // WM throws a SecurityException if the display is untrusted. - if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) - == Display.FLAG_TRUSTED) { - mWindowManager.setDisplayImePolicy(displayId, - WindowManager.DISPLAY_IME_POLICY_LOCAL); - } + void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { + mWindowManager.setDisplayImePolicy(displayId, policy); } @GuardedBy("mLock") 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 45d7314f3fab..58aa2c303345 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -104,6 +104,7 @@ import com.android.server.LocalServices; import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; import com.android.server.companion.virtual.audio.VirtualAudioController; import com.android.server.companion.virtual.camera.VirtualCameraController; +import com.android.server.inputmethod.InputMethodManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -352,6 +353,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; } mBaseVirtualDisplayFlags = flags; + + if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) { + final String imeId = mParams.getInputMethodComponent().flattenToShortString(); + Slog.d(TAG, "Setting custom input method " + imeId + " as default for virtual device " + + deviceId); + InputMethodManagerInternal.get().setVirtualDeviceInputMethodForAllUsers( + mDeviceId, imeId); + } } @VisibleForTesting @@ -556,6 +565,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mCameraAccessController.stopObservingIfNeeded(); } + // Clear any previously set custom IME components. + if (Flags.vdmCustomIme() && mParams.getInputMethodComponent() != null) { + InputMethodManagerInternal.get().setVirtualDeviceInputMethodForAllUsers( + mDeviceId, null); + } + mInputController.close(); mSensorController.close(); } finally { @@ -884,6 +899,24 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + public void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { + super.setDisplayImePolicy_enforcePermission(); + synchronized (mVirtualDeviceLock) { + if (!mVirtualDisplays.contains(displayId)) { + throw new SecurityException("Display ID " + displayId + + " not found for this virtual device"); + } + } + final long ident = Binder.clearCallingIdentity(); + try { + mInputController.setDisplayImePolicy(displayId, policy); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) @Nullable public List<VirtualSensor> getVirtualSensorList() { super.getVirtualSensorList_enforcePermission(); @@ -1080,7 +1113,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mInputController.setPointerAcceleration(1f, displayId); mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, displayId); - mInputController.setLocalIme(displayId); + // WM throws a SecurityException if the display is untrusted. + if ((mDisplayManagerInternal.getDisplayInfo(displayId).flags & Display.FLAG_TRUSTED) + == Display.FLAG_TRUSTED) { + mInputController.setDisplayImePolicy(displayId, + WindowManager.DISPLAY_IME_POLICY_LOCAL); + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 9b78ed41206d..923728ffb0f3 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -859,6 +859,11 @@ public class VirtualDeviceManagerService extends SystemService { } @Override + public boolean isValidVirtualDeviceId(int deviceId) { + return mImpl.isValidVirtualDeviceId(deviceId); + } + + @Override public @Nullable String getPersistentIdForDevice(int deviceId) { if (deviceId == Context.DEVICE_ID_DEFAULT) { return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT; diff --git a/services/core/Android.bp b/services/core/Android.bp index b4cf34e00c53..20a3b9ada85d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -154,7 +154,6 @@ java_library_static { static_libs: [ "android.frameworks.location.altitude-V1-java", // AIDL - "android.frameworks.vibrator-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", "android.hardware.boot-V1.0-java", // HIDL diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index e9d4d76c6131..33726d17da62 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -17,6 +17,8 @@ package com.android.server; import static android.os.Flags.stateOfHealthPublic; +import static android.os.Flags.batteryServiceSupportCurrentAdbCommand; + import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; import static com.android.server.health.Utils.copyV1Battery; @@ -166,6 +168,7 @@ public final class BatteryService extends SystemService { private int mLowBatteryCloseWarningLevel; private int mBatteryNearlyFullLevel; private int mShutdownBatteryTemperature; + private boolean mShutdownIfNoPower; private static String sSystemUiPackage; @@ -230,6 +233,8 @@ public final class BatteryService extends SystemService { com.android.internal.R.integer.config_lowBatteryCloseWarningBump); mShutdownBatteryTemperature = mContext.getResources().getInteger( com.android.internal.R.integer.config_shutdownBatteryTemperature); + mShutdownIfNoPower = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_shutdownIfNoPower); sSystemUiPackage = mContext.getResources().getString( com.android.internal.R.string.config_systemUi); @@ -391,6 +396,9 @@ public final class BatteryService extends SystemService { if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) { return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL); } + if (!mShutdownIfNoPower) { + return false; + } if (mHealthInfo.batteryLevel > 0) { return false; } @@ -920,9 +928,12 @@ public final class BatteryService extends SystemService { pw.println("Battery service (battery) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" get [-f] [ac|usb|wireless|dock|status|level|temp|present|counter|invalid]"); - pw.println(" set [-f] " - + "[ac|usb|wireless|dock|status|level|temp|present|counter|invalid] <value>"); + String getSetOptions = "ac|usb|wireless|dock|status|level|temp|present|counter|invalid"; + if (batteryServiceSupportCurrentAdbCommand()) { + getSetOptions += "|current_now|current_average"; + } + pw.println(" get [-f] [" + getSetOptions + "]"); + pw.println(" set [-f] [" + getSetOptions + "] <value>"); pw.println(" Force a battery property value, freezing battery state."); pw.println(" -f: force a battery change broadcast be sent, prints new sequence."); pw.println(" unplug [-f]"); @@ -994,6 +1005,16 @@ public final class BatteryService extends SystemService { case "counter": pw.println(mHealthInfo.batteryChargeCounterUah); break; + case "current_now": + if (batteryServiceSupportCurrentAdbCommand()) { + pw.println(mHealthInfo.batteryCurrentMicroamps); + } + break; + case "current_average": + if (batteryServiceSupportCurrentAdbCommand()) { + pw.println(mHealthInfo.batteryCurrentAverageMicroamps); + } + break; case "temp": pw.println(mHealthInfo.batteryTemperatureTenthsCelsius); break; @@ -1051,6 +1072,16 @@ public final class BatteryService extends SystemService { case "counter": mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value); break; + case "current_now": + if (batteryServiceSupportCurrentAdbCommand()) { + mHealthInfo.batteryCurrentMicroamps = Integer.parseInt(value); + } + break; + case "current_average": + if (batteryServiceSupportCurrentAdbCommand()) { + mHealthInfo.batteryCurrentAverageMicroamps = + Integer.parseInt(value); + } case "temp": mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value); break; diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 4bb9f4f27b64..c436c7217d0f 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -127,6 +127,7 @@ public class Watchdog implements Dumpable { "/system/bin/mediaserver", "/system/bin/netd", "/system/bin/sdcard", + "/system/bin/servicemanager", "/system/bin/surfaceflinger", "/system/bin/vold", "media.extractor", // system/bin/mediaextractor diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2d687de2f734..ac173f3b5b7a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1710,19 +1710,23 @@ public class ActivityManagerService extends IActivityManager.Stub private static final int INDEX_NATIVE_PSS = 0; private static final int INDEX_NATIVE_SWAP_PSS = 1; private static final int INDEX_NATIVE_RSS = 2; - private static final int INDEX_DALVIK_PSS = 3; - private static final int INDEX_DALVIK_SWAP_PSS = 4; - private static final int INDEX_DALVIK_RSS = 5; - private static final int INDEX_OTHER_PSS = 6; - private static final int INDEX_OTHER_SWAP_PSS = 7; - private static final int INDEX_OTHER_RSS = 8; - private static final int INDEX_TOTAL_PSS = 9; - private static final int INDEX_TOTAL_SWAP_PSS = 10; - private static final int INDEX_TOTAL_RSS = 11; - private static final int INDEX_TOTAL_NATIVE_PSS = 12; - private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 13; - private static final int INDEX_TOTAL_MEMTRACK_GL = 14; - private static final int INDEX_LAST = 15; + private static final int INDEX_NATIVE_PRIVATE_DIRTY = 3; + private static final int INDEX_DALVIK_PSS = 4; + private static final int INDEX_DALVIK_SWAP_PSS = 5; + private static final int INDEX_DALVIK_RSS = 6; + private static final int INDEX_DALVIK_PRIVATE_DIRTY = 7; + private static final int INDEX_OTHER_PSS = 8; + private static final int INDEX_OTHER_SWAP_PSS = 9; + private static final int INDEX_OTHER_RSS = 10; + private static final int INDEX_OTHER_PRIVATE_DIRTY = 11; + private static final int INDEX_TOTAL_PSS = 12; + private static final int INDEX_TOTAL_SWAP_PSS = 13; + private static final int INDEX_TOTAL_RSS = 14; + private static final int INDEX_TOTAL_PRIVATE_DIRTY = 15; + private static final int INDEX_TOTAL_NATIVE_PSS = 16; + private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 17; + private static final int INDEX_TOTAL_MEMTRACK_GL = 18; + private static final int INDEX_LAST = 19; /** * Used to notify activity lifecycle events. @@ -11712,13 +11716,14 @@ public class ActivityManagerService extends IActivityManager.Stub final long pss; final long swapPss; final long mRss; + final long mPrivateDirty; final int id; // pid final int userId; final boolean hasActivities; ArrayList<MemItem> subitems; - MemItem(String label, String shortLabel, long pss, long swapPss, long rss, int id, - @UserIdInt int userId, + MemItem(String label, String shortLabel, long pss, long swapPss, long rss, + long privateDirty, int id, @UserIdInt int userId, boolean hasActivities) { this.isProc = true; this.label = label; @@ -11726,18 +11731,21 @@ public class ActivityManagerService extends IActivityManager.Stub this.pss = pss; this.swapPss = swapPss; this.mRss = rss; + this.mPrivateDirty = privateDirty; this.id = id; this.userId = userId; this.hasActivities = hasActivities; } - MemItem(String label, String shortLabel, long pss, long swapPss, long rss, int id) { + MemItem(String label, String shortLabel, long pss, long swapPss, long rss, + long privateDirty, int id) { this.isProc = false; this.label = label; this.shortLabel = shortLabel; this.pss = pss; this.swapPss = swapPss; this.mRss = rss; + this.mPrivateDirty = privateDirty; this.id = id; this.userId = UserHandle.USER_SYSTEM; this.hasActivities = false; @@ -11762,7 +11770,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final void dumpMemItems(PrintWriter pw, String prefix, String tag, ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpPss, - boolean dumpSwapPss) { + boolean dumpSwapPss, boolean dumpPrivateDirty) { if (sort && !isCompact) { sortMemItems(items, dumpPss); } @@ -11770,14 +11778,18 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i=0; i<items.size(); i++) { MemItem mi = items.get(i); if (!isCompact) { - if (dumpPss && dumpSwapPss) { - pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss), - mi.label, stringifyKBSize(mi.swapPss)); - } else { - pw.printf("%s%s: %s%s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss), + pw.printf("%s%s: %s%s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss), mi.label, mi.userId != UserHandle.USER_SYSTEM ? " (user " + mi.userId + ")" : ""); + if (dumpPss && dumpSwapPss) { + pw.printf("(%s in swap%s", stringifyKBSize(mi.swapPss), + dumpPrivateDirty ? ", " : ")"); } + if (dumpPrivateDirty) { + pw.printf("%s%s private dirty)", dumpSwapPss ? "" : "(", + stringifyKBSize(mi.mPrivateDirty)); + } + pw.printf("\n"); } else if (mi.isProc) { pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(","); pw.print(mi.id); pw.print(","); @@ -11791,7 +11803,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (mi.subitems != null) { dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems, - true, isCompact, dumpPss, dumpSwapPss); + true, isCompact, dumpPss, dumpSwapPss, dumpPrivateDirty); } } } @@ -11959,6 +11971,8 @@ public class ActivityManagerService extends IActivityManager.Stub boolean isCheckinRequest; boolean dumpSwapPss; boolean dumpProto; + boolean mDumpPrivateDirty; + boolean mDumpAllocatorStats; } @NeverCompile // Avoid size overhead of debugging code. @@ -11977,6 +11991,8 @@ public class ActivityManagerService extends IActivityManager.Stub opts.isCheckinRequest = false; opts.dumpSwapPss = false; opts.dumpProto = asProto; + opts.mDumpPrivateDirty = false; + opts.mDumpAllocatorStats = false; int opti = 0; while (opti < args.length) { @@ -11999,6 +12015,8 @@ public class ActivityManagerService extends IActivityManager.Stub opts.dumpSummaryOnly = true; } else if ("-S".equals(opt)) { opts.dumpSwapPss = true; + } else if ("-p".equals(opt)) { + opts.mDumpPrivateDirty = true; } else if ("--unreachable".equals(opt)) { opts.dumpUnreachable = true; } else if ("--oom".equals(opt)) { @@ -12011,7 +12029,8 @@ public class ActivityManagerService extends IActivityManager.Stub opts.isCheckinRequest = true; } else if ("--proto".equals(opt)) { opts.dumpProto = true; - + } else if ("--logstats".equals(opt)) { + opts.mDumpAllocatorStats = true; } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]"); pw.println(" -a: include all available information for each process."); @@ -12019,6 +12038,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" -c: dump in a compact machine-parseable representation."); pw.println(" -s: dump only summary of application memory usage."); pw.println(" -S: dump also SwapPss."); + pw.println(" -p: dump also private dirty memory usage."); pw.println(" --oom: only show processes organized by oom adj."); pw.println(" --local: only collect details locally, don't call process."); pw.println(" --package: interpret process arg as package, dumping all"); @@ -12137,14 +12157,18 @@ public class ActivityManagerService extends IActivityManager.Stub EmptyArray.LONG; long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG; + long[] dalvikSubitemPrivateDirty = opts.dumpDalvik + ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; + long[] miscPrivateDirty = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long[] memtrackTmp = new long[4]; long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length]; long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length]; + long[] oomPrivateDirty = new long[DUMP_MEM_OOM_LABEL.length]; ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) new ArrayList[DUMP_MEM_OOM_LABEL.length]; @@ -12217,7 +12241,8 @@ public class ActivityManagerService extends IActivityManager.Stub try { thread.dumpMemInfo(tp.getWriteFd(), mi, opts.isCheckinRequest, opts.dumpFullDetails, - opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs); + opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, + opts.mDumpAllocatorStats, innerArgs); tp.go(fd, opts.dumpUnreachable ? 30000 : 5000); } finally { tp.kill(); @@ -12240,6 +12265,7 @@ public class ActivityManagerService extends IActivityManager.Stub final long myTotalUss = mi.getTotalUss(); final long myTotalRss = mi.getTotalRss(); final long myTotalSwapPss = mi.getTotalSwappedOutPss(); + final long myTotalPrivateDirty = mi.getTotalPrivateDirty(); synchronized (mProcLock) { if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) { @@ -12253,29 +12279,36 @@ public class ActivityManagerService extends IActivityManager.Stub ss[INDEX_TOTAL_PSS] += myTotalPss; ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss; ss[INDEX_TOTAL_RSS] += myTotalRss; + ss[INDEX_TOTAL_PRIVATE_DIRTY] += myTotalPrivateDirty; ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics; ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl; MemItem pssItem = new MemItem(r.processName + " (pid " + pid + (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, - myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities); + myTotalSwapPss, myTotalRss, myTotalPrivateDirty, + pid, r.userId, hasActivities); procMems.add(pssItem); procMemsMap.put(pid, pssItem); ss[INDEX_NATIVE_PSS] += mi.nativePss; ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss; ss[INDEX_NATIVE_RSS] += mi.nativeRss; + ss[INDEX_NATIVE_PRIVATE_DIRTY] += mi.nativePrivateDirty; ss[INDEX_DALVIK_PSS] += mi.dalvikPss; ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss; ss[INDEX_DALVIK_RSS] += mi.dalvikRss; + ss[INDEX_DALVIK_PRIVATE_DIRTY] += mi.dalvikPrivateDirty; for (int j=0; j<dalvikSubitemPss.length; j++) { dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); dalvikSubitemSwapPss[j] += mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikSubitemPrivateDirty[j] += + mi.getOtherPrivateDirty(Debug.MemoryInfo.NUM_OTHER_STATS + j); dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j); } ss[INDEX_OTHER_PSS] += mi.otherPss; ss[INDEX_OTHER_RSS] += mi.otherRss; ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss; + ss[INDEX_OTHER_PRIVATE_DIRTY] += mi.otherPrivateDirty; for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = mi.getOtherPss(j); miscPss[j] += mem; @@ -12283,6 +12316,9 @@ public class ActivityManagerService extends IActivityManager.Stub mem = mi.getOtherSwappedOutPss(j); miscSwapPss[j] += mem; ss[INDEX_OTHER_SWAP_PSS] -= mem; + mem = mi.getOtherPrivateDirty(j); + miscPrivateDirty[j] += mem; + ss[INDEX_OTHER_PRIVATE_DIRTY] -= mem; mem = mi.getOtherRss(j); miscRss[j] += mem; ss[INDEX_OTHER_RSS] -= mem; @@ -12299,6 +12335,7 @@ public class ActivityManagerService extends IActivityManager.Stub && oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) { oomPss[oomIndex] += myTotalPss; oomSwapPss[oomIndex] += myTotalSwapPss; + oomPrivateDirty[oomIndex] += myTotalPrivateDirty; if (oomProcs[oomIndex] == null) { oomProcs[oomIndex] = new ArrayList<MemItem>(); } @@ -12345,6 +12382,7 @@ public class ActivityManagerService extends IActivityManager.Stub final long myTotalPss = info.getTotalPss(); final long myTotalSwapPss = info.getTotalSwappedOutPss(); final long myTotalRss = info.getTotalRss(); + final long myTotalPrivateDirty = info.getTotalPrivateDirty(); ss[INDEX_TOTAL_PSS] += myTotalPss; ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss; ss[INDEX_TOTAL_RSS] += myTotalRss; @@ -12354,15 +12392,17 @@ public class ActivityManagerService extends IActivityManager.Stub MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss, - st.pid, UserHandle.getUserId(st.uid), false); + myTotalPrivateDirty, st.pid, UserHandle.getUserId(st.uid), false); procMems.add(pssItem); ss[INDEX_NATIVE_PSS] += info.nativePss; ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss; ss[INDEX_NATIVE_RSS] += info.nativeRss; + ss[INDEX_NATIVE_PRIVATE_DIRTY] += info.nativePrivateDirty; ss[INDEX_DALVIK_PSS] += info.dalvikPss; ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss; ss[INDEX_DALVIK_RSS] += info.dalvikRss; + ss[INDEX_DALVIK_PRIVATE_DIRTY] += info.dalvikPrivateDirty; for (int j = 0; j < dalvikSubitemPss.length; j++) { dalvikSubitemPss[j] += info.getOtherPss( Debug.MemoryInfo.NUM_OTHER_STATS + j); @@ -12370,10 +12410,13 @@ public class ActivityManagerService extends IActivityManager.Stub info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikSubitemPrivateDirty[j] += + info.getOtherPrivateDirty(Debug.MemoryInfo.NUM_OTHER_STATS + j); } ss[INDEX_OTHER_PSS] += info.otherPss; ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss; ss[INDEX_OTHER_RSS] += info.otherRss; + ss[INDEX_OTHER_PRIVATE_DIRTY] += info.otherPrivateDirty; for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = info.getOtherPss(j); miscPss[j] += mem; @@ -12384,6 +12427,9 @@ public class ActivityManagerService extends IActivityManager.Stub mem = info.getOtherRss(j); miscRss[j] += mem; ss[INDEX_OTHER_RSS] -= mem; + mem = info.getOtherPrivateDirty(j); + miscPrivateDirty[j] += mem; + ss[INDEX_OTHER_PRIVATE_DIRTY] -= mem; } oomPss[0] += myTotalPss; oomSwapPss[0] += myTotalSwapPss; @@ -12392,21 +12438,26 @@ public class ActivityManagerService extends IActivityManager.Stub } oomProcs[0].add(pssItem); oomRss[0] += myTotalRss; + oomPrivateDirty[0] += myTotalPrivateDirty; } }); ArrayList<MemItem> catMems = new ArrayList<MemItem>(); catMems.add(new MemItem("Native", "Native", - ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1)); + ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], + ss[INDEX_NATIVE_RSS], ss[INDEX_NATIVE_PRIVATE_DIRTY], -1)); final int dalvikId = -2; catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS], - ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId)); + ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], + ss[INDEX_DALVIK_PRIVATE_DIRTY], dalvikId)); catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS], - ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3)); + ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], + ss[INDEX_OTHER_PRIVATE_DIRTY], -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { String label = Debug.MemoryInfo.getOtherLabel(j); - catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j)); + catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], + miscPrivateDirty[j], j)); } if (dalvikSubitemPss.length > 0) { // Add dalvik subitems. @@ -12432,7 +12483,8 @@ public class ActivityManagerService extends IActivityManager.Stub final String name = Debug.MemoryInfo.getOtherLabel( Debug.MemoryInfo.NUM_OTHER_STATS + j); memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], - dalvikSubitemSwapPss[j], dalvikSubitemRss[j], j)); + dalvikSubitemSwapPss[j], dalvikSubitemRss[j], + dalvikSubitemPrivateDirty[j], j)); } } } @@ -12443,7 +12495,7 @@ public class ActivityManagerService extends IActivityManager.Stub String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] : DUMP_MEM_OOM_LABEL[j]; MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j], - DUMP_MEM_OOM_ADJ[j]); + oomPrivateDirty[j], DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; oomMems.add(item); } @@ -12454,33 +12506,34 @@ public class ActivityManagerService extends IActivityManager.Stub if (!brief && !opts.oomOnly && !opts.isCompact) { pw.println(); pw.println("Total RSS by process:"); - dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, false, false); + dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, false, false, false); pw.println(); } if (!opts.isCompact) { pw.println("Total RSS by OOM adjustment:"); } - dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, false, false); + dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, false, false, false); if (!brief && !opts.oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; if (!opts.isCompact) { out.println(); out.println("Total RSS by category:"); } - dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false); + dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false, false); } opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0; if (!brief && !opts.oomOnly && !opts.isCompact) { pw.println(); pw.println("Total PSS by process:"); dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, true, - opts.dumpSwapPss); + opts.dumpSwapPss, opts.mDumpPrivateDirty); pw.println(); } if (!opts.isCompact) { pw.println("Total PSS by OOM adjustment:"); } - dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, true, opts.dumpSwapPss); + dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, true, opts.dumpSwapPss, + opts.mDumpPrivateDirty); if (!brief && !opts.oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; if (!opts.isCompact) { @@ -12488,7 +12541,7 @@ public class ActivityManagerService extends IActivityManager.Stub out.println("Total PSS by category:"); } dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, true, - opts.dumpSwapPss); + opts.dumpSwapPss, opts.mDumpPrivateDirty); } if (!opts.isCompact) { pw.println(); @@ -12890,7 +12943,7 @@ public class ActivityManagerService extends IActivityManager.Stub ss[INDEX_TOTAL_RSS] += myTotalRss; MemItem pssItem = new MemItem(r.processName + " (pid " + pid + (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, - myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities); + myTotalSwapPss, myTotalRss, 0, pid, r.userId, hasActivities); procMems.add(pssItem); procMemsMap.put(pid, pssItem); @@ -12977,7 +13030,7 @@ public class ActivityManagerService extends IActivityManager.Stub ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss; MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", - st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss, + st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss, 0, st.pid, UserHandle.getUserId(st.uid), false); procMems.add(pssItem); @@ -13022,15 +13075,16 @@ public class ActivityManagerService extends IActivityManager.Stub ArrayList<MemItem> catMems = new ArrayList<MemItem>(); catMems.add(new MemItem("Native", "Native", ss[INDEX_NATIVE_PSS], - ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1)); + ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], 0, -1)); final int dalvikId = -2; catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS], - ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId)); + ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], 0, dalvikId)); catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS], - ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3)); + ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], 0, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { String label = Debug.MemoryInfo.getOtherLabel(j); - catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j)); + catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], + miscRss[j], 0, j)); } if (dalvikSubitemPss.length > 0) { // Add dalvik subitems. @@ -13056,7 +13110,7 @@ public class ActivityManagerService extends IActivityManager.Stub final String name = Debug.MemoryInfo.getOtherLabel( Debug.MemoryInfo.NUM_OTHER_STATS + j); memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], - dalvikSubitemSwapPss[j], dalvikSubitemRss[j], j)); + dalvikSubitemSwapPss[j], dalvikSubitemRss[j], 0, j)); } } } @@ -13066,7 +13120,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (oomPss[j] != 0) { String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] : DUMP_MEM_OOM_LABEL[j]; - MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j], + MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j], 0, DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; oomMems.add(item); @@ -17460,6 +17514,8 @@ public class ActivityManagerService extends IActivityManager.Stub * other {@code ActivityManager#USER_OP_*} codes for failure. * */ + // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows + // delayed locking behavior once the private space flag is finalized. @Override public int stopUserWithDelayedLocking(final int userId, boolean force, final IStopUserCallback callback) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 47a99fe24ec4..a6b532cdef09 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -356,6 +356,8 @@ class UserController implements Handler.Callback { * Once total number of unlocked users reach mMaxRunningUsers, least recently used user * will be locked. */ + // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows + // delayed locking behavior once the private space flag is finalized. @GuardedBy("mLock") private boolean mDelayUserDataLocking; @@ -365,11 +367,12 @@ class UserController implements Handler.Callback { private volatile boolean mAllowUserUnlocking; /** - * Keep track of last active users for mDelayUserDataLocking. - * The latest stopped user is placed in front while the least recently stopped user in back. + * Keep track of last active users for delayUserDataLocking. + * The most recently stopped user with delayed locking is placed in front, while the least + * recently stopped user in back. */ @GuardedBy("mLock") - private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>(); + private final ArrayList<Integer> mLastActiveUsersForDelayedLocking = new ArrayList<>(); /** * Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet- @@ -1011,20 +1014,21 @@ class UserController implements Handler.Callback { Slogf.i(TAG, "stopSingleUserLU userId=" + userId); final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started - // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock - // the requested user as the client wants to stop and lock the user. On the other hand, - // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking - // is set as that means client wants to lock the user immediately. - // If mDelayUserDataLocking is not set, the user was already locked when it was stopped - // and no further action is necessary. - if (mDelayUserDataLocking) { + // If canDelayDataLockingForUser() is true and allowDelayedLocking is false, we need + // to lock the requested user as the client wants to stop and lock the user. On the + // other hand, having keyEvictedCallback set will lead into locking user if + // canDelayDataLockingForUser() is true as that means client wants to lock the user + // immediately. + // If canDelayDataLockingForUser() is false, the user was already locked when it was + // stopped and no further action is necessary. + if (canDelayDataLockingForUser(userId)) { if (allowDelayedLocking && keyEvictedCallback != null) { Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it" + " and lock user:" + userId, new RuntimeException()); allowDelayedLocking = false; } if (!allowDelayedLocking) { - if (mLastActiveUsers.remove(Integer.valueOf(userId))) { + if (mLastActiveUsersForDelayedLocking.remove(Integer.valueOf(userId))) { // should lock the user, user is already gone final ArrayList<KeyEvictedCallback> keyEvictedCallbacks; if (keyEvictedCallback != null) { @@ -1354,14 +1358,21 @@ class UserController implements Handler.Callback { @GuardedBy("mLock") private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) { int userIdToLock = userId; - if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral() + // TODO: Decouple the delayed locking flows from mMaxRunningUsers or rename the property to + // state maximum running unlocked users specifically + if (canDelayDataLockingForUser(userIdToLock) && allowDelayedLocking + && !getUserInfo(userId).isEphemeral() && !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) { - mLastActiveUsers.remove((Integer) userId); // arg should be object, not index - mLastActiveUsers.add(0, userId); - int totalUnlockedUsers = mStartedUsers.size() + mLastActiveUsers.size(); + // arg should be object, not index + mLastActiveUsersForDelayedLocking.remove((Integer) userId); + mLastActiveUsersForDelayedLocking.add(0, userId); + int totalUnlockedUsers = mStartedUsers.size() + + mLastActiveUsersForDelayedLocking.size(); if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user - userIdToLock = mLastActiveUsers.get(mLastActiveUsers.size() - 1); - mLastActiveUsers.remove(mLastActiveUsers.size() - 1); + userIdToLock = mLastActiveUsersForDelayedLocking.get( + mLastActiveUsersForDelayedLocking.size() - 1); + mLastActiveUsersForDelayedLocking + .remove(mLastActiveUsersForDelayedLocking.size() - 1); Slogf.i(TAG, "finishUserStopped, stopping user:" + userId + " lock user:" + userIdToLock); } else { @@ -1374,6 +1385,24 @@ class UserController implements Handler.Callback { } /** + * Returns whether the user can have its CE storage left unlocked, even when it is stopped, + * either due to a global device configuration or an individual user's property. + */ + private boolean canDelayDataLockingForUser(@UserIdInt int userIdToLock) { + if (allowBiometricUnlockForPrivateProfile()) { + final UserProperties userProperties = getUserProperties(userIdToLock); + return (mDelayUserDataLocking || (userProperties != null + && userProperties.getAllowStoppingUserWithDelayedLocking())); + } + return mDelayUserDataLocking; + } + + private boolean allowBiometricUnlockForPrivateProfile() { + return android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace(); + } + + /** * Determines the list of users that should be stopped together with the specified * {@code userId}. The returned list includes {@code userId}. */ @@ -3161,7 +3190,7 @@ class UserController implements Handler.Callback { pw.println(" mCurrentProfileIds:" + Arrays.toString(mCurrentProfileIds)); pw.println(" mCurrentUserId:" + mCurrentUserId); pw.println(" mTargetUserId:" + mTargetUserId); - pw.println(" mLastActiveUsers:" + mLastActiveUsers); + pw.println(" mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking); pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking); pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking); pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch()); diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index e91b7e8e37fc..b1a12f7338da 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -37,6 +37,7 @@ import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING; import static android.app.AppOpsManager.flagsToString; import static android.app.AppOpsManager.getUidStateName; @@ -136,7 +137,7 @@ final class DiscreteRegistry { private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + "," + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + "," + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," - + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO; + + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO + "," + OP_RESERVED_FOR_TESTING; private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis(); private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis(); private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION = diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java index ffdab7dfbfa4..9ae43a03515a 100644 --- a/services/core/java/com/android/server/audio/AdiDeviceState.java +++ b/services/core/java/com/android/server/audio/AdiDeviceState.java @@ -235,9 +235,10 @@ public final class AdiDeviceState { * {@link AdiDeviceState#toPersistableString()}. */ public static int getPeristedMaxSize() { - return 36; /* (mDeviceType)2 + (mDeviceAddress)17 + (mInternalDeviceType)9 + (mSAEnabled)1 + return 39; /* (mDeviceType)2 + (mDeviceAddress)17 + (mInternalDeviceType)9 + (mSAEnabled)1 + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1 - + (SETTINGS_FIELD_SEPARATOR)5 */ + + (mAudioDeviceCategory)1 + (SETTINGS_FIELD_SEPARATOR)6 + + (SETTING_DEVICE_SEPARATOR)1 */ } @Nullable diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 9cfcb1679429..80917533cce1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -2037,9 +2037,8 @@ public class AudioDeviceBroker { } break; case MSG_L_UPDATED_ADI_DEVICE_STATE: - synchronized (mDeviceStateLock) { - mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj); - } break; + mAudioService.onUpdatedAdiDeviceState((AdiDeviceState) msg.obj); + break; default: Log.wtf(TAG, "Invalid message " + msg.what); @@ -2687,11 +2686,15 @@ public class AudioDeviceBroker { return; } final SettingsAdapter settingsAdapter = mAudioService.getSettings(); - boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(), - Settings.Secure.AUDIO_DEVICE_INVENTORY, - deviceSettings, UserHandle.USER_CURRENT); - if (!res) { - Log.e(TAG, "error saving AdiDeviceState: " + deviceSettings); + try { + boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(), + Settings.Secure.AUDIO_DEVICE_INVENTORY, + deviceSettings, UserHandle.USER_CURRENT); + if (!res) { + Log.e(TAG, "error saving AdiDeviceState: " + deviceSettings); + } + } catch (IllegalArgumentException e) { + Log.e(TAG, "error saving AdiDeviceState: " + deviceSettings, e); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e54bf64df09e..34cfdfaa7974 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -30,7 +30,6 @@ import static android.media.AudioSystem.isBluetoothOutDevice; import static android.media.AudioSystem.isBluetoothScoOutDevice; import static android.media.audio.Flags.automaticBtDeviceType; - import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.annotation.NonNull; @@ -81,7 +80,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -104,6 +102,14 @@ public class AudioDeviceInventory { private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|"; private static final String SETTING_DEVICE_SEPARATOR = "\\|"; + /** Max String length that can be persisted within the Settings. */ + // LINT.IfChange(settings_max_length_per_string) + private static final int MAX_SETTINGS_LENGTH_PER_STRING = 32768; + // LINT.ThenChange(/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java) + + private static final int MAX_DEVICE_INVENTORY_ENTRIES = + MAX_SETTINGS_LENGTH_PER_STRING / AdiDeviceState.getPeristedMaxSize(); + // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); @@ -113,7 +119,8 @@ public class AudioDeviceInventory { private final Object mDeviceInventoryLock = new Object(); @GuardedBy("mDeviceInventoryLock") - private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>(); + private final LinkedHashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = + new LinkedHashMap<>(); Collection<AdiDeviceState> getImmutableDeviceInventory() { final List<AdiDeviceState> newList; @@ -136,6 +143,7 @@ public class AudioDeviceInventory { oldState.setSAEnabled(newState.isSAEnabled()); return oldState; }); + checkDeviceInventorySize_l(); } mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState); } @@ -173,6 +181,8 @@ public class AudioDeviceInventory { ads.setAudioDeviceCategory(category); mDeviceInventory.put(ads.getDeviceId(), ads); + checkDeviceInventorySize_l(); + mDeviceBroker.postUpdatedAdiDeviceState(ads); mDeviceBroker.postPersistAudioDeviceSettings(); } @@ -200,6 +210,7 @@ public class AudioDeviceInventory { } return oldState; }); + checkDeviceInventorySize_l(); } if (updatedCategory.get()) { mDeviceBroker.postUpdatedAdiDeviceState(deviceState); @@ -272,6 +283,18 @@ public class AudioDeviceInventory { } } + @GuardedBy("mDeviceInventoryLock") + private void checkDeviceInventorySize_l() { + if (mDeviceInventory.size() > MAX_DEVICE_INVENTORY_ENTRIES) { + // remove the first element + Iterator<Entry<Pair<Integer, String>, AdiDeviceState>> iterator = + mDeviceInventory.entrySet().iterator(); + if (iterator.hasNext()) { + iterator.remove(); + } + } + } + @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"}) private boolean synchronizeBleDeviceInInventory(AdiDeviceState updatedDevice) { for (DeviceInfo di : mConnectedDevices.values()) { @@ -1501,7 +1524,7 @@ public class AudioDeviceInventory { } else { status = addOp.deviceRoleAction(useCase, role, devices); if (status == AudioSystem.SUCCESS) { - rolesMap.put(key, devices); + rolesMap.put(key, new ArrayList(devices)); } } return status; @@ -2806,7 +2829,7 @@ public class AudioDeviceInventory { deviceCatalogSize = mDeviceInventory.size(); final StringBuilder settingsBuilder = new StringBuilder( - deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); + deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator(); if (iterator.hasNext()) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f7b7aaa60a35..44cb1367928d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -13779,6 +13779,11 @@ public class AudioService extends IAudioService.Stub return mDeviceBroker.getDeviceAddresses(device); } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + MusicFxHelper getMusicFxHelper() { + return mMusicFxHelper; + } + //====================== // misc //====================== diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java index 5f4e4c3bc4e0..85b3b49ecf78 100644 --- a/services/core/java/com/android/server/audio/MusicFxHelper.java +++ b/services/core/java/com/android/server/audio/MusicFxHelper.java @@ -17,9 +17,11 @@ package com.android.server.audio; import static android.content.pm.PackageManager.MATCH_ANY_USER; + import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.IUidObserver; import android.app.UidObserver; @@ -48,7 +50,6 @@ import java.util.List; /** * MusicFx management. - * . */ public class MusicFxHelper { private static final String TAG = "AS.MusicFxHelper"; @@ -60,14 +61,113 @@ public class MusicFxHelper { // Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver. private final Object mClientUidMapLock = new Object(); + private final String mPackageName = this.getClass().getPackage().getName(); + + private final String mMusicFxPackageName = "com.android.musicfx"; + + /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1; + // The binder token identifying the UidObserver registration. private IBinder mUidObserverToken = null; + // Package name and list of open audio sessions for this package + private static class PackageSessions { + String mPackageName; + List<Integer> mSessions; + } + + /* + * Override of SparseArray class to add bind/unbind and UID observer in the put/remove methods. + * + * put: + * - the first key/value set put into MySparseArray will trigger a procState bump (bindService) + * - if no valid observer token exist, will call registerUidObserver for put + * - for each new uid put into array, it will be added to uid observer list + * + * remove: + * - for each uid removed from array, it will be removed from uid observer list as well + * - if it's the last uid in array, no more MusicFx procState bump (unbindService), uid + * observer will also be removed, and observer token reset to null + */ + private class MySparseArray extends SparseArray<PackageSessions> { + private final String mMusicFxPackageName = "com.android.musicfx"; + + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) + @Override + public void put(int uid, PackageSessions pkgSessions) { + if (size() == 0) { + int procState = ActivityManager.PROCESS_STATE_NONEXISTENT; + try { + procState = ActivityManager.getService().getPackageProcessState( + mMusicFxPackageName, mPackageName); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException with getPackageProcessState: " + e); + } + if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + Intent bindIntent = new Intent().setClassName(mMusicFxPackageName, + "com.android.musicfx.KeepAliveService"); + mContext.bindServiceAsUser( + bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE, + UserHandle.of(getCurrentUserId())); + Log.i(TAG, "bindService to " + mMusicFxPackageName); + } + + Log.i(TAG, mMusicFxPackageName + " procState " + procState); + } + try { + if (mUidObserverToken == null) { + mUidObserverToken = ActivityManager.getService().registerUidObserverForUids( + mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE, + ActivityManager.PROCESS_STATE_UNKNOWN, mPackageName, + new int[]{uid}); + Log.i(TAG, "registered to observer with UID " + uid); + } else if (get(uid) == null) { // addUidToObserver if this is a new UID + ActivityManager.getService().addUidToObserver(mUidObserverToken, mPackageName, + uid); + Log.i(TAG, " UID " + uid + " add to observer"); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException with UID observer add/register: " + e); + } + + super.put(uid, pkgSessions); + } + + @Override + public void remove(int uid) { + if (get(uid) != null) { + try { + ActivityManager.getService().removeUidFromObserver(mUidObserverToken, + mPackageName, uid); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException with removeUidFromObserver: " + e); + } + } + + super.remove(uid); + + // stop foreground service delegate and unregister UID observers with the last UID + if (size() == 0) { + try { + ActivityManager.getService().unregisterUidObserver(mEffectUidObserver); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException with unregisterUidObserver: " + e); + } + mUidObserverToken = null; + mContext.unbindService(mMusicFxBindConnection); + Log.i(TAG, "last session closed, unregister UID observer, and unbind " + + mMusicFxPackageName); + } + } + } + // Hashmap of UID and list of open sessions for this UID. @GuardedBy("mClientUidMapLock") - private SparseArray<List<Integer>> mClientUidSessionMap = new SparseArray<>(); - - /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1; + private MySparseArray mClientUidSessionMap = new MySparseArray(); // UID observer for effect MusicFx clients private final IUidObserver mEffectUidObserver = new UidObserver() { @@ -102,23 +202,27 @@ public class MusicFxHelper { * Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. * + * Only intents without target application package {@link android.content.Intent#getPackage} + * will be handled by the MusicFxHelper, all intents handled and forwarded by MusicFxHelper + * will have the target application package. + * * If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}: - * - If the MusicFx process is not running, call bindService with AUTO_CREATE to create. - * - If this is the first audio session in MusicFx, call set foreground service delegate. + * - If the MusicFx process is not running, call bindServiceAsUser with AUTO_CREATE to create. + * - If this is the first audio session of MusicFx, call set foreground service delegate. * - If this is the first audio session for a given UID, add the UID into observer. * - * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}: - * - MusicFx will not be foreground delegated anymore. - * - The KeepAliveService of MusicFx will be unbound. - * - The UidObserver will be removed. + * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} + * - The KeepAliveService of MusicFx will be unbound, and MusicFx will not be foreground + * delegated anymore if the last session of the last package was closed. + * - The Uid Observer will be removed when the last session of a package was closed. */ + @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS}) public void handleAudioEffectBroadcast(Context context, Intent intent) { String target = intent.getPackage(); if (target != null) { Log.w(TAG, "effect broadcast already targeted to " + target); return; } - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); final PackageManager pm = context.getPackageManager(); // TODO this should target a user-selected panel List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */); @@ -126,14 +230,14 @@ public class MusicFxHelper { ResolveInfo ri = ril.get(0); final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME); try { - final int senderUid = pm.getPackageUidAsUser(senderPackageName, - PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId()); if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) { + final int senderUid = pm.getPackageUidAsUser(senderPackageName, + PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId()); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.setPackage(ri.activityInfo.packageName); - synchronized (mClientUidMapLock) { - setMusicFxServiceWithObserver(context, intent, senderUid); + if (setMusicFxServiceWithObserver(intent, senderUid, senderPackageName)) { + context.sendBroadcastAsUser(intent, UserHandle.ALL); } - context.sendBroadcastAsUser(intent, UserHandle.ALL); return; } } catch (PackageManager.NameNotFoundException e) { @@ -144,127 +248,105 @@ public class MusicFxHelper { Log.w(TAG, "couldn't find receiver package for effect intent"); } - /** - * Handle the UidObserver onUidGone callback of MusicFx clients. - * All open audio sessions of this UID will be closed. - * If this is the last UID for MusicFx: - * - MusicFx will not be foreground delegated anymore. - * - The KeepAliveService of MusicFx will be unbound. - * - The UidObserver will be removed. - */ - public void handleEffectClientUidGone(int uid) { - synchronized (mClientUidMapLock) { - Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE"); - // Once the uid is no longer running, close all remain audio session(s) for this UID - if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) { - final List<Integer> sessions = - new ArrayList(mClientUidSessionMap.get(Integer.valueOf(uid))); - Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions"); - for (Integer session : sessions) { - Intent intent = new Intent( - AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); - intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, session); - setMusicFxServiceWithObserver(mContext, intent, uid); - Log.i(TAG, "Close session " + session + " of UID " + uid); - } - mClientUidSessionMap.remove(Integer.valueOf(uid)); + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) + @GuardedBy("mClientUidMapLock") + private boolean handleAudioEffectSessionOpen( + int senderUid, String senderPackageName, int sessionId) { + Log.d(TAG, senderPackageName + " UID " + senderUid + " open MusicFx session " + sessionId); + + PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); + if (pkgSessions != null && pkgSessions.mSessions != null) { + if (pkgSessions.mSessions.contains(sessionId)) { + Log.e(TAG, "Audio session " + sessionId + " already open for UID: " + + senderUid + ", package: " + senderPackageName + ", abort"); + return false; + } + if (pkgSessions.mPackageName != senderPackageName) { + Log.w(TAG, "Inconsistency package names for UID open: " + senderUid + " prev: " + + pkgSessions.mPackageName + ", now: " + senderPackageName); + return false; } + } else { + // first session for this UID, create a new Package/Sessions pair + pkgSessions = new PackageSessions(); + pkgSessions.mSessions = new ArrayList(); + pkgSessions.mPackageName = senderPackageName; } + + pkgSessions.mSessions.add(Integer.valueOf(sessionId)); + mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions); + return true; } + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) @GuardedBy("mClientUidMapLock") - private void setMusicFxServiceWithObserver(Context context, Intent intent, int senderUid) { - PackageManager pm = context.getPackageManager(); - try { - final int audioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, - AudioManager.AUDIO_SESSION_ID_GENERATE); - if (AudioManager.AUDIO_SESSION_ID_GENERATE == audioSession) { - Log.e(TAG, "Intent missing audio session: " + audioSession); - return; + private boolean handleAudioEffectSessionClose( + int senderUid, String senderPackageName, int sessionId) { + Log.d(TAG, senderPackageName + " UID " + senderUid + " close MusicFx session " + sessionId); + + PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); + if (pkgSessions == null) { + Log.e(TAG, senderPackageName + " UID " + senderUid + " does not exist in map, abort"); + return false; + } + if (pkgSessions.mPackageName != senderPackageName) { + Log.w(TAG, "Inconsistency package names for UID " + senderUid + " close, prev: " + + pkgSessions.mPackageName + ", now: " + senderPackageName); + return false; + } + + if (pkgSessions.mSessions != null && pkgSessions.mSessions.size() != 0) { + if (!pkgSessions.mSessions.contains(sessionId)) { + Log.e(TAG, senderPackageName + " UID " + senderUid + " session " + sessionId + + " does not exist in map, abort"); + return false; } - // only apply to com.android.musicfx and KeepAliveService for now - final String musicFxPackageName = "com.android.musicfx"; - final String musicFxKeepAliveService = "com.android.musicfx.KeepAliveService"; - final int musicFxUid = pm.getPackageUidAsUser(musicFxPackageName, - PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId()); + pkgSessions.mSessions.remove(Integer.valueOf(sessionId)); + } - if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) { - List<Integer> sessions = new ArrayList<>(); - Log.d(TAG, "UID " + senderUid + ", open MusicFx session " + audioSession); - // start foreground service delegate and register UID observer with the first - // session of first UID open - if (0 == mClientUidSessionMap.size()) { - final int procState = ActivityManager.getService().getPackageProcessState( - musicFxPackageName, this.getClass().getPackage().getName()); - // if musicfx process not in binding state, call bindService with AUTO_CREATE - if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - Intent bindIntent = new Intent().setClassName(musicFxPackageName, - musicFxKeepAliveService); - context.bindServiceAsUser( - bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE, - UserHandle.of(getCurrentUserId())); - Log.i(TAG, "bindService to " + musicFxPackageName); - } + if (pkgSessions.mSessions == null || pkgSessions.mSessions.size() == 0) { + // remove UID from map as well as the UID observer with the last session close + mClientUidSessionMap.remove(Integer.valueOf(senderUid)); + } else { + mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions); + } - Log.i(TAG, "Package " + musicFxPackageName + " uid " + musicFxUid - + " procState " + procState); - } else if (mClientUidSessionMap.get(Integer.valueOf(senderUid)) != null) { - sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); - if (sessions.contains(audioSession)) { - Log.e(TAG, "Audio session " + audioSession + " already exist for UID " - + senderUid + ", abort"); - return; - } - } - // first session of this UID - if (sessions.size() == 0) { - // call registerUidObserverForUids with the first UID and first session - if (mClientUidSessionMap.size() == 0 || mUidObserverToken == null) { - mUidObserverToken = ActivityManager.getService().registerUidObserverForUids( - mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE, - ActivityManager.PROCESS_STATE_UNKNOWN, null, new int[]{senderUid}); - Log.i(TAG, "UID " + senderUid + " registered to observer"); - } else { - // add UID to observer for each new UID - ActivityManager.getService().addUidToObserver(mUidObserverToken, TAG, - senderUid); - Log.i(TAG, "UID " + senderUid + " addeded to observer"); - } - } + return true; + } - sessions.add(Integer.valueOf(audioSession)); - mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions); - } else { - if (mClientUidSessionMap.get(senderUid) != null) { - Log.d(TAG, "UID " + senderUid + ", close MusicFx session " + audioSession); - List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid)); - sessions.remove(Integer.valueOf(audioSession)); - if (0 == sessions.size()) { - mClientUidSessionMap.remove(Integer.valueOf(senderUid)); - } else { - mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions); - } + /** + * @return true if the intent is validated and handled successfully, false with any error + * (invalid sender/intent for example). + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) + private boolean setMusicFxServiceWithObserver( + Intent intent, int senderUid, String packageName) { + final int session = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, + AudioManager.AUDIO_SESSION_ID_GENERATE); + if (AudioManager.AUDIO_SESSION_ID_GENERATE == session) { + Log.e(TAG, packageName + " intent have no invalid audio session"); + return false; + } - // stop foreground service delegate and unregister UID observer with the - // last session of last UID close - if (0 == mClientUidSessionMap.size()) { - ActivityManager.getService().unregisterUidObserver(mEffectUidObserver); - mClientUidSessionMap.clear(); - context.unbindService(mMusicFxBindConnection); - Log.i(TAG, " remove all sessions, unregister UID observer, and unbind " - + musicFxPackageName); - } - } else { - // if the audio session already closed, print an error - Log.e(TAG, "UID " + senderUid + " close audio session " + audioSession - + " which does not exist"); - } + synchronized (mClientUidMapLock) { + if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) { + return handleAudioEffectSessionOpen(senderUid, packageName, session); + } else { + return handleAudioEffectSessionClose(senderUid, packageName, session); } - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Not able to find UID from package: " + e); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException " + e + " with handling intent"); } } @@ -281,6 +363,39 @@ public class MusicFxHelper { return UserHandle.USER_SYSTEM; } + + /** + * Handle the UidObserver onUidGone callback of MusicFx clients. + * Send close intent for all open audio sessions of this UID. The mClientUidSessionMap will be + * updated with the handling of close intent in setMusicFxServiceWithObserver. + */ + @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS}) + private void handleEffectClientUidGone(int uid) { + synchronized (mClientUidMapLock) { + Log.d(TAG, "handle MSG_EFFECT_CLIENT_GONE uid: " + uid + " mapSize: " + + mClientUidSessionMap.size()); + // Once the uid is no longer running, close all remain audio session(s) for this UID + final PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(uid)); + if (pkgSessions != null) { + Log.i(TAG, "UID " + uid + " gone, closing all sessions"); + + // send close intent for each open session of the gone UID + for (Integer sessionId : pkgSessions.mSessions) { + Intent closeIntent = + new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + closeIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, pkgSessions.mPackageName); + closeIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId); + closeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + // set broadcast target + closeIntent.setPackage(mMusicFxPackageName); + mContext.sendBroadcastAsUser(closeIntent, UserHandle.ALL); + } + mClientUidSessionMap.remove(Integer.valueOf(uid)); + } + } + } + + @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS}) /*package*/ void handleMessage(Message msg) { switch (msg.what) { case MSG_EFFECT_CLIENT_GONE: @@ -292,5 +407,4 @@ public class MusicFxHelper { break; } } - } diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java index 394e4af30a9e..e012d17e2e3f 100644 --- a/services/core/java/com/android/server/audio/RotationHelper.java +++ b/services/core/java/com/android/server/audio/RotationHelper.java @@ -80,6 +80,7 @@ class RotationHelper { sContext = context; sHandler = handler; sDisplayListener = new AudioDisplayListener(); + sFoldStateListener = new FoldStateListener(sContext, RotationHelper::updateFoldState); sRotationCallback = rotationCallback; sFoldStateCallback = foldStateCallback; enable(); @@ -90,7 +91,6 @@ class RotationHelper { .registerDisplayListener(sDisplayListener, sHandler); updateOrientation(); - sFoldStateListener = new FoldStateListener(sContext, folded -> updateFoldState(folded)); sContext.getSystemService(DeviceStateManager.class) .registerCallback(new HandlerExecutor(sHandler), sFoldStateListener); } diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index cecde55ef89f..823788f0b249 100644 --- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.VirtualDevice; import android.companion.virtual.sensor.VirtualSensor; +import android.content.Context; import android.os.LocaleList; import android.util.ArraySet; @@ -149,6 +150,14 @@ public abstract class VirtualDeviceManagerInternal { public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId); /** + * Checks whether the passed {@code deviceId} is a valid virtual device ID or not. + * + * <p>{@link Context#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default + * device which is not a virtual device.</p> + */ + public abstract boolean isValidVirtualDeviceId(int deviceId); + + /** * Returns the ID of the device which owns the display with the given ID. * * <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 575b30946fce..1e5e147749a2 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -105,6 +105,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper; +import com.android.internal.content.PackageMonitor; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; @@ -438,19 +439,23 @@ public class SyncManager { } }; - private final BroadcastReceiver mForceStoppedReceiver = new BroadcastReceiver() { + private static class PackageMonitorImpl extends PackageMonitor { @Override - public void onReceive(Context context, Intent intent) { + public boolean onHandleForceStop(Intent intent, String[] packageNames, int uid, + boolean doit, Bundle extras) { final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); - // For now, just log when packages were force-stopped and unstopped for debugging. if (isLoggable) { - if (Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())) { - Log.d(TAG, "Package force-stopped: " - + intent.getData().getSchemeSpecificPart()); - } else if (Intent.ACTION_PACKAGE_UNSTOPPED.equals(intent.getAction())) { - Log.d(TAG, "Package unstopped: " - + intent.getData().getSchemeSpecificPart()); - } + Log.d(TAG, "Package force-stopped: " + Arrays.toString(packageNames) + + ", uid: " + uid); + } + return false; + } + + @Override + public void onPackageUnstopped(String packageName, int uid, Bundle extras) { + final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); + if (isLoggable) { + Log.d(TAG, "Package unstopped: " + packageName + ", uid: " + uid); } } }; @@ -718,11 +723,10 @@ public class SyncManager { mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); - intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - intentFilter.addAction(Intent.ACTION_PACKAGE_UNSTOPPED); - intentFilter.addDataScheme("package"); - context.registerReceiver(mForceStoppedReceiver, intentFilter); + + final PackageMonitor packageMonitor = new PackageMonitorImpl(); + packageMonitor.register(mContext, null /* thread */, UserHandle.ALL, + false /* externalStorage */); intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED); context.registerReceiver(mOtherIntentsReceiver, intentFilter); @@ -1288,7 +1292,11 @@ public class SyncManager { */ private boolean isPackageStopped(String packageName, int userId) { if (android.content.pm.Flags.stayStopped()) { - return mPackageManagerInternal.isPackageStopped(packageName, userId); + try { + return mPackageManagerInternal.isPackageStopped(packageName, userId); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + } } return false; } diff --git a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java index f57bf295a34e..405c14941442 100644 --- a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java +++ b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java @@ -130,8 +130,8 @@ public class DisplayNotificationManager implements ConnectedDisplayUsbErrorsDete } sendErrorNotification(createErrorNotification( - R.string.connected_display_cable_dont_support_displays_notification_title, - R.string.connected_display_cable_dont_support_displays_notification_content, + R.string.connected_display_unavailable_notification_title, + R.string.connected_display_unavailable_notification_content, R.drawable.usb_cable_unknown_issue)); } diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java index 017c86d4b363..d2504164a6df 100644 --- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java +++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java @@ -29,6 +29,12 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { // State to wait for the <Active Source> message. private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1; + // Number of retries <Request Active Source> is sent if no device answers this message. + private static final int MAX_SEND_RETRY_COUNT = 1; + + private int mSendRetryCount = 0; + + RequestActiveSourceAction(HdmiCecLocalDevice source, IHdmiControlCallback callback) { super(source, callback); } @@ -60,7 +66,12 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { return; } if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) { - finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); + if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) { + sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); + addTimer(mState, HdmiConfig.TIMEOUT_MS); + } else { + finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); + } } } } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java index dcb86a7d1eb6..66807aeb6629 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java @@ -23,7 +23,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UiThread; import android.content.ComponentName; +import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.hardware.input.InputManager; import android.hardware.input.InputManagerGlobal; import android.os.Handler; import android.os.IBinder; @@ -64,6 +66,7 @@ final class HandwritingModeController { private static final int LONG_EVENT_BUFFER_SIZE = EVENT_BUFFER_SIZE * 20; private static final long HANDWRITING_DELEGATION_IDLE_TIMEOUT_MS = 3000; + private final Context mContext; // This must be the looper for the UiThread. private final Looper mLooper; private final InputManagerInternal mInputManagerInternal; @@ -87,7 +90,9 @@ final class HandwritingModeController { private int mCurrentRequestId; @AnyThread - HandwritingModeController(Looper uiThreadLooper, Runnable inkWindowInitRunnable) { + HandwritingModeController(Context context, Looper uiThreadLooper, + Runnable inkWindowInitRunnable) { + mContext = context; mLooper = uiThreadLooper; mCurrentDisplayId = Display.INVALID_DISPLAY; mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); @@ -285,7 +290,14 @@ final class HandwritingModeController { mHandwritingSurface.startIntercepting(imePid, imeUid); // Unset the pointer icon for the stylus in case the app had set it. - InputManagerGlobal.getInstance().setPointerIconType(PointerIcon.TYPE_NOT_SPECIFIED); + if (com.android.input.flags.Flags.enablePointerChoreographer()) { + Objects.requireNonNull(mContext.getSystemService(InputManager.class)).setPointerIcon( + PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_NOT_SPECIFIED), + downEvent.getDisplayId(), downEvent.getDeviceId(), downEvent.getPointerId(0), + mHandwritingSurface.getInputChannel().getToken()); + } else { + InputManagerGlobal.getInstance().setPointerIconType(PointerIcon.TYPE_NOT_SPECIFIED); + } return new HandwritingSession(mCurrentRequestId, mHandwritingSurface.getInputChannel(), mHandwritingBuffer); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 14daf62a9ed2..f526dbe9c66d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -120,6 +120,25 @@ public abstract class InputMethodManagerInternal { @UserIdInt int userId); /** + * Makes the input method associated with {@code imeId} the default input method for all users + * on displays that are owned by the virtual device with the given {@code deviceId}. If the + * input method associated with {@code imeId} is not available, there will be no IME on the + * relevant displays. + * + * <p>The caller of this method is responsible for resetting it to {@code null} after the + * virtual device is closed.</p> + * + * @param deviceId the device ID on which to use the given input method as default. + * @param imeId the input method ID to be used as default on the given device. If {@code null}, + * then any existing input method association with that device will be removed. + * @throws IllegalArgumentException if a non-{@code null} input method ID is passed for a + * device ID that already has a custom input method set or if + * the device ID is not a valid virtual device. + */ + public abstract void setVirtualDeviceInputMethodForAllUsers( + int deviceId, @Nullable String imeId); + + /** * Registers a new {@link InputMethodListListener}. * * @param listener the listener to add @@ -159,9 +178,12 @@ public abstract class InputMethodManagerInternal { /** * Updates the IME visibility, back disposition and show IME picker status for SystemUI. * TODO(b/189923292): Making SystemUI to be true IME icon controller vs. presenter that - * controlled by IMMS. + * controlled by IMMS. + * + * @param disableImeIcon indicates whether IME icon should be enabled or not + * @param displayId the display for which to update the IME window status */ - public abstract void updateImeWindowStatus(boolean disableImeIcon); + public abstract void updateImeWindowStatus(boolean disableImeIcon, int displayId); /** * Finish stylus handwriting by calling {@link InputMethodService#finishStylusHandwriting()} if @@ -247,6 +269,11 @@ public abstract class InputMethodManagerInternal { } @Override + public void setVirtualDeviceInputMethodForAllUsers( + int deviceId, @Nullable String imeId) { + } + + @Override public void registerInputMethodListListener(InputMethodListListener listener) { } @@ -269,7 +296,7 @@ public abstract class InputMethodManagerInternal { } @Override - public void updateImeWindowStatus(boolean disableImeIcon) { + public void updateImeWindowStatus(boolean disableImeIcon, int displayId) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b7007854dab8..2d145c2d9b15 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -169,6 +169,7 @@ import com.android.internal.os.TransferPipe; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.internal.view.IInputMethodManager; import com.android.server.AccessibilityManagerInternal; import com.android.server.EventLogTags; @@ -312,6 +313,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // All known input methods. final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>(); private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>(); + // Mapping from deviceId to the device-specific imeId for that device. + private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>(); + final InputMethodSubtypeSwitchingController mSwitchingController; final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(); @@ -1689,7 +1693,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mLastSwitchUserId = userId; // mSettings should be created before buildInputMethodListLocked - mSettings = new InputMethodSettings(mContext, mMethodMap, userId, !mSystemReady); + mSettings = new InputMethodSettings(mMethodMap, userId, !mSystemReady); AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId); mSwitchingController = @@ -1709,7 +1713,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor); mNonPreemptibleInputMethods = mRes.getStringArray( com.android.internal.R.array.config_nonPreemptibleInputMethods); - mHwController = new HandwritingModeController(thread.getLooper(), + mHwController = new HandwritingModeController(mContext, thread.getLooper(), new InkWindowInitializer()); registerDeviceListenerAndCheckStylusSupport(); } @@ -2019,8 +2023,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub //TODO(b/197848765): This can be optimized by caching multi-user methodMaps/methodList. //TODO(b/210039666): use cache. final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, - userId, true); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true); final InputMethodInfo imi = methodMap.get(settings.getSelectedInputMethod()); return imi != null && imi.supportsStylusHandwriting(); } @@ -2056,7 +2059,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, methodList, directBootAwareness); - settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */); + settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */); } // filter caller's access to input methods methodList.removeIf(imi -> @@ -2074,7 +2077,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub settings = mSettings; } else { final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */); + settings = new InputMethodSettings(methodMap, userId, true /* copyOnWrite */); methodList = settings.getEnabledInputMethodListLocked(); } // filter caller's access to input methods @@ -2154,8 +2157,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (imi == null) { return Collections.emptyList(); } - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId, - true); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true); if (!canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)) { return Collections.emptyList(); } @@ -4132,8 +4134,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, - userId, false); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); return settings.getLastInputMethodSubtypeLocked(); } } @@ -4185,8 +4186,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, methodList, DirectBootAwareness.AUTO); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, - userId, false); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid); } @@ -4215,7 +4215,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final boolean currentUser = (mSettings.getCurrentUserId() == userId); final InputMethodSettings settings = currentUser ? mSettings - : new InputMethodSettings(mContext, queryMethodMapForUser(userId), userId, + : new InputMethodSettings(queryMethodMapForUser(userId), userId, !mUserManagerInternal.isUserUnlocked(userId)); if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) { return; @@ -5365,8 +5365,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, - userId, false); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); return settings.getCurrentInputMethodSubtypeForNonCurrentUsers(); } } @@ -5439,8 +5438,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, methodList, DirectBootAwareness.AUTO); - InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId, - true /* copyOnWrite */); + InputMethodSettings settings = new InputMethodSettings(methodMap, userId, true); return methodMap.get(settings.getSelectedInputMethod()); } @@ -5467,8 +5465,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return true; } final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId, - false); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); if (!methodMap.containsKey(imeId) || !settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) { return false; // IME is not found or not enabled. @@ -5607,7 +5604,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return true; } final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); if (!methodMap.containsKey(imeId)) { return false; // IME is not found. @@ -5627,6 +5624,23 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override + public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) { + // TODO(b/287269288): validate that id belongs to a valid virtual device instead. + Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT, + "DeviceId " + deviceId + " does not belong to a virtual device."); + synchronized (ImfLock.class) { + if (imeId == null) { + mVirtualDeviceMethodMap.remove(deviceId); + } else if (mVirtualDeviceMethodMap.contains(deviceId)) { + throw new IllegalArgumentException("Virtual device " + deviceId + + " already has a custom input method component"); + } else { + mVirtualDeviceMethodMap.put(deviceId, imeId); + } + } + } + + @Override public void registerInputMethodListListener(InputMethodListListener listener) { mInputMethodListListeners.addIfAbsent(listener); } @@ -5674,7 +5688,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void updateImeWindowStatus(boolean disableImeIcon) { + public void updateImeWindowStatus(boolean disableImeIcon, int displayId) { mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0) .sendToTarget(); } @@ -6331,8 +6345,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } else { final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, - userId, false); + final InputMethodSettings settings = new InputMethodSettings(methodMap, userId, false); if (enabled) { if (!methodMap.containsKey(imeId)) { failedToEnableUnknownIme = true; @@ -6476,7 +6489,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, methodList, DirectBootAwareness.AUTO); - final InputMethodSettings settings = new InputMethodSettings(mContext, + final InputMethodSettings settings = new InputMethodSettings( methodMap, userId, false); nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index b4338f6b2bd5..acf9a7f9ae63 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -208,8 +208,6 @@ final class InputMethodUtils { */ @UserHandleAware public static class InputMethodSettings { - @NonNull - private Context mUserAwareContext; private final ArrayMap<String, InputMethodInfo> mMethodMap; private boolean mCopyOnWrite = false; @@ -253,17 +251,9 @@ final class InputMethodUtils { return imsList; } - private void initContentWithUserContext(@NonNull Context context, @UserIdInt int userId) { - mUserAwareContext = context.getUserId() == userId - ? context - : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); - } - - InputMethodSettings(@NonNull Context context, - ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId, + InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId, boolean copyOnWrite) { mMethodMap = methodMap; - initContentWithUserContext(context, userId); switchCurrentUser(userId, copyOnWrite); } @@ -282,9 +272,6 @@ final class InputMethodUtils { if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) { mEnabledInputMethodsStrCache = ""; } - if (mUserAwareContext.getUserId() != userId) { - initContentWithUserContext(mUserAwareContext, userId); - } mCurrentUserId = userId; mCopyOnWrite = copyOnWrite; } diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index d359280c2a6f..bab3cbea108e 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -424,6 +424,7 @@ public abstract class IContextHubWrapper { // 9a17008d-6bf1-445a-9011-6d21bd985b6c private static final byte[] UUID = {-102, 23, 0, -115, 107, -15, 68, 90, -112, 17, 109, 33, -67, -104, 91, 108}; + private static final String NAME = "ContextHubService"; ContextHubAidlCallback(int contextHubId, ICallback callback) { mContextHubId = contextHubId; @@ -466,10 +467,14 @@ public abstract class IContextHubWrapper { // TODO(271471342): Implement } - public byte[] getUuid() throws RemoteException { + public byte[] getUuid() { return UUID; } + public String getName() { + return NAME; + } + @Override public String getInterfaceHash() { return android.hardware.contexthub.IContextHubCallback.HASH; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 49095ceb5bdd..42c2548264d4 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1432,7 +1432,7 @@ public class LockSettingsService extends ILockSettings.Stub { } private void unlockKeystore(int userId, SyntheticPassword sp) { - Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null); + Authorization.onDeviceUnlocked(userId, sp.deriveKeyStorePassword()); } @VisibleForTesting /** Note: this method is overridden in unit tests */ diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java index a00999d08b5b..7cf3983e57f6 100644 --- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java +++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java @@ -39,6 +39,7 @@ import android.util.SparseBooleanArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.media.flags.Flags; import java.util.ArrayList; import java.util.HashMap; @@ -219,7 +220,10 @@ import java.util.stream.Collectors; BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo(); newBtRoute.mBtDevice = device; - String deviceName = device.getName(); + String deviceName = + Flags.enableUseOfBluetoothDeviceGetAliasForMr2infoGetName() + ? device.getAlias() + : device.getName(); if (TextUtils.isEmpty(deviceName)) { deviceName = mContext.getResources().getText(R.string.unknownName).toString(); } diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java index 041fceaf8d3d..ede2d274563e 100644 --- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java +++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java @@ -43,6 +43,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.internal.R; +import com.android.media.flags.Flags; import java.util.ArrayList; import java.util.HashMap; @@ -283,7 +284,10 @@ class LegacyBluetoothRouteController implements BluetoothRouteController { newBtRoute.mBtDevice = device; String routeId = device.getAddress(); - String deviceName = device.getName(); + String deviceName = + Flags.enableUseOfBluetoothDeviceGetAliasForMr2infoGetName() + ? device.getAlias() + : device.getName(); if (TextUtils.isEmpty(deviceName)) { deviceName = mContext.getResources().getText(R.string.unknownName).toString(); } diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java index 9fdeda4b4bd0..885566693b9a 100644 --- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java +++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java @@ -16,14 +16,23 @@ package com.android.server.notification; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; + import android.app.UiModeManager; import android.app.WallpaperManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.display.ColorDisplayManager; import android.os.Binder; import android.os.PowerManager; import android.service.notification.DeviceEffectsApplier; import android.service.notification.ZenDeviceEffects; +import android.service.notification.ZenModeConfig; +import android.service.notification.ZenModeConfig.ConfigChangeOrigin; + +import com.android.internal.annotations.GuardedBy; /** Default implementation for {@link DeviceEffectsApplier}. */ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier { @@ -34,13 +43,24 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier { private static final int SATURATION_LEVEL_FULL_COLOR = 100; private static final float WALLPAPER_DIM_AMOUNT_DIMMED = 0.6f; private static final float WALLPAPER_DIM_AMOUNT_NORMAL = 0f; + private static final IntentFilter SCREEN_OFF_INTENT_FILTER = new IntentFilter( + Intent.ACTION_SCREEN_OFF); + private final Context mContext; private final ColorDisplayManager mColorDisplayManager; private final PowerManager mPowerManager; private final UiModeManager mUiModeManager; private final WallpaperManager mWallpaperManager; + private final Object mRegisterReceiverLock = new Object(); + @GuardedBy("mRegisterReceiverLock") + private boolean mIsScreenOffReceiverRegistered; + + private ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build(); + private boolean mPendingNightMode; + DefaultDeviceEffectsApplier(Context context) { + mContext = context; mColorDisplayManager = context.getSystemService(ColorDisplayManager.class); mPowerManager = context.getSystemService(PowerManager.class); mUiModeManager = context.getSystemService(UiModeManager.class); @@ -48,24 +68,90 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier { } @Override - public void apply(ZenDeviceEffects effects) { + public void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int origin) { Binder.withCleanCallingIdentity(() -> { - mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN, - effects.shouldSuppressAmbientDisplay()); + if (mLastAppliedEffects.shouldSuppressAmbientDisplay() + != effects.shouldSuppressAmbientDisplay()) { + mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN, + effects.shouldSuppressAmbientDisplay()); + } - if (mColorDisplayManager != null) { - mColorDisplayManager.setSaturationLevel( - effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE - : SATURATION_LEVEL_FULL_COLOR); + if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) { + if (mColorDisplayManager != null) { + mColorDisplayManager.setSaturationLevel( + effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE + : SATURATION_LEVEL_FULL_COLOR); + } } - if (mWallpaperManager != null) { - mWallpaperManager.setWallpaperDimAmount( - effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED - : WALLPAPER_DIM_AMOUNT_NORMAL); + if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) { + if (mWallpaperManager != null) { + mWallpaperManager.setWallpaperDimAmount( + effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED + : WALLPAPER_DIM_AMOUNT_NORMAL); + } } - // TODO: b/308673343 - Apply dark theme (via UiModeManager) when screen is off. + if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) { + updateOrScheduleNightMode(effects.shouldUseNightMode(), origin); + } }); + + mLastAppliedEffects = effects; + } + + private void updateOrScheduleNightMode(boolean useNightMode, @ConfigChangeOrigin int origin) { + mPendingNightMode = useNightMode; + + // Changing the theme can be disruptive for the user (Activities are likely recreated, may + // lose some state). Therefore we only apply the change immediately if the rule was + // activated manually, or we are initializing, or the screen is currently off/dreaming. + if (origin == ZenModeConfig.UPDATE_ORIGIN_INIT + || origin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER + || origin == ZenModeConfig.UPDATE_ORIGIN_USER + || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + || !mPowerManager.isInteractive()) { + unregisterScreenOffReceiver(); + updateNightModeImmediately(useNightMode); + } else { + registerScreenOffReceiver(); + } + } + + @GuardedBy("mRegisterReceiverLock") + private final BroadcastReceiver mNightModeWhenScreenOff = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + unregisterScreenOffReceiver(); + updateNightModeImmediately(mPendingNightMode); + } + }; + + private void updateNightModeImmediately(boolean useNightMode) { + Binder.withCleanCallingIdentity(() -> { + // TODO: b/314285749 - Placeholder; use real APIs when available. + mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME, + useNightMode); + }); + } + + private void registerScreenOffReceiver() { + synchronized (mRegisterReceiverLock) { + if (!mIsScreenOffReceiverRegistered) { + mContext.registerReceiver(mNightModeWhenScreenOff, SCREEN_OFF_INTENT_FILTER, + Context.RECEIVER_NOT_EXPORTED); + mIsScreenOffReceiverRegistered = true; + } + } + } + + private void unregisterScreenOffReceiver() { + synchronized (mRegisterReceiverLock) { + if (mIsScreenOffReceiverRegistered) { + mIsScreenOffReceiverRegistered = false; + mContext.unregisterReceiver(mNightModeWhenScreenOff); + } + } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 02845fb03119..c2a145df4114 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5299,11 +5299,11 @@ public class NotificationManagerService extends SystemService { public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException { enforceSystemOrSystemUI("INotificationManager.setZenMode"); final int callingUid = Binder.getCallingUid(); - final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi(); final long identity = Binder.clearCallingIdentity(); try { - mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid, - isSystemOrSystemUi); + mZenModeHelper.setManualZenMode(mode, conditionId, + ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, // Checked by enforce() + reason, /* caller= */ null, callingUid); } finally { Binder.restoreCallingIdentity(identity); } @@ -5360,10 +5360,11 @@ public class NotificationManagerService extends SystemService { } return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule, - "addAutomaticZenRule", Binder.getCallingUid(), - // TODO: b/308670715: Distinguish FROM_APP from FROM_USER - isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI - : ZenModeHelper.FROM_APP); + // TODO: b/308670715: Distinguish origin properly (e.g. USER if creating a rule + // manually in Settings). + isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + "addAutomaticZenRule", Binder.getCallingUid()); } @Override @@ -5379,11 +5380,12 @@ public class NotificationManagerService extends SystemService { Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null"); enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule"); + // TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule + // manually in Settings). return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule, - "updateAutomaticZenRule", Binder.getCallingUid(), - // TODO: b/308670715: Distinguish FROM_APP from FROM_USER - isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI - : ZenModeHelper.FROM_APP); + isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + "updateAutomaticZenRule", Binder.getCallingUid()); } @Override @@ -5392,8 +5394,12 @@ public class NotificationManagerService extends SystemService { // Verify that they can modify zen rules. enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule"); - return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule", - Binder.getCallingUid(), isCallerSystemOrSystemUi()); + // TODO: b/308670715: Distinguish origin properly (e.g. USER if removing a rule + // manually in Settings). + return mZenModeHelper.removeAutomaticZenRule(id, + isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + "removeAutomaticZenRule", Binder.getCallingUid()); } @Override @@ -5402,8 +5408,9 @@ public class NotificationManagerService extends SystemService { enforceSystemOrSystemUI("removeAutomaticZenRules"); return mZenModeHelper.removeAutomaticZenRules(packageName, - packageName + "|removeAutomaticZenRules", Binder.getCallingUid(), - isCallerSystemOrSystemUi()); + isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + packageName + "|removeAutomaticZenRules", Binder.getCallingUid()); } @Override @@ -5421,8 +5428,12 @@ public class NotificationManagerService extends SystemService { enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState"); - mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(), - isCallerSystemOrSystemUi()); + // TODO: b/308670715: Distinguish origin properly (e.g. USER if toggling a rule + // manually in Settings). + mZenModeHelper.setAutomaticZenRuleState(id, condition, + isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + Binder.getCallingUid()); } @Override @@ -5440,8 +5451,11 @@ public class NotificationManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { - mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter", - callingUid, isSystemOrSystemUi); + mZenModeHelper.setManualZenMode(zen, null, + isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + /* reason= */ "setInterruptionFilter", /* caller= */ pkg, + callingUid); } finally { Binder.restoreCallingIdentity(identity); } @@ -5806,7 +5820,10 @@ public class NotificationManagerService extends SystemService { } else { ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy); - mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi); + mZenModeHelper.setNotificationPolicy(policy, + isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + callingUid); } } catch (RemoteException e) { Slog.e(TAG, "Failed to set notification policy", e); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 6a7eebb32c8b..fd86870e5163 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -59,6 +59,7 @@ import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; import android.service.notification.RankingHelperProto; +import android.service.notification.ZenModeConfig; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; @@ -1862,12 +1863,16 @@ public class PreferencesHelper implements RankingConfig { public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid, boolean fromSystemOrSystemUi) { NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy(); - mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy( - policy.priorityCategories, policy.priorityCallSenders, - policy.priorityMessageSenders, policy.suppressedVisualEffects, - (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND - : 0), - policy.priorityConversationSenders), callingUid, fromSystemOrSystemUi); + mZenModeHelper.setNotificationPolicy( + new NotificationManager.Policy( + policy.priorityCategories, policy.priorityCallSenders, + policy.priorityMessageSenders, policy.suppressedVisualEffects, + (areChannelsBypassingDnd + ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND : 0), + policy.priorityConversationSenders), + fromSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + callingUid); } // TODO: b/310620812 - rename to hasPriorityChannels() when modes_api is inlined. diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 6ecd799bf8e6..86aa2d8b0a10 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -111,8 +111,10 @@ public class ZenModeConditions implements ConditionProviders.Callback { public void onServiceAdded(ComponentName component) { if (DEBUG) Log.d(TAG, "onServiceAdded " + component); final int callingUid = Binder.getCallingUid(); - mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component, - callingUid, callingUid == Process.SYSTEM_UID); + mHelper.setConfig(mHelper.getConfig(), component, + callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + "zmc.onServiceAdded:" + component, callingUid); } @Override @@ -121,8 +123,10 @@ public class ZenModeConditions implements ConditionProviders.Callback { ZenModeConfig config = mHelper.getConfig(); if (config == null) return; final int callingUid = Binder.getCallingUid(); - mHelper.setAutomaticZenRuleState(id, condition, callingUid, - callingUid == Process.SYSTEM_UID); + mHelper.setAutomaticZenRuleState(id, condition, + callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + : ZenModeConfig.UPDATE_ORIGIN_APP, + callingUid); } // Only valid for CPS backed rules diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 218519fef68b..7ec94c315798 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,11 +24,16 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.service.notification.NotificationServiceProto.ROOT_CONFIG; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import android.annotation.DrawableRes; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -79,6 +84,7 @@ import android.service.notification.ConditionProviderService; import android.service.notification.DeviceEffectsApplier; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; +import android.service.notification.ZenModeConfig.ConfigChangeOrigin; import android.service.notification.ZenModeConfig.ZenRule; import android.service.notification.ZenModeProto; import android.service.notification.ZenPolicy; @@ -111,8 +117,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -137,21 +141,6 @@ public class ZenModeHelper { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L; - /** A rule addition or update that is initiated by the System or SystemUI. */ - static final int FROM_SYSTEM_OR_SYSTEMUI = 1; - /** A rule addition or update that is initiated by the user (through system settings). */ - static final int FROM_USER = 2; - /** A rule addition or update that is initiated by an app (via NotificationManager APIs). */ - static final int FROM_APP = 3; - - @IntDef(prefix = { "FROM_" }, value = { - FROM_SYSTEM_OR_SYSTEMUI, - FROM_USER, - FROM_APP - }) - @Retention(RetentionPolicy.SOURCE) - @interface ChangeOrigin {} - // pkg|userId => uid @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>(); @@ -160,7 +149,7 @@ public class ZenModeHelper { private final SettingsObserver mSettingsObserver; private final AppOpsManager mAppOps; private final NotificationManager mNotificationManager; - private ZenModeConfig mDefaultConfig; + private final ZenModeConfig mDefaultConfig; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final ZenModeFiltering mFiltering; private final RingerModeDelegate mRingerModeDelegate = new @@ -275,9 +264,8 @@ public class ZenModeHelper { // "update" config to itself, which will have no effect in the case where a config // was read in via XML, but will initialize zen mode if nothing was read in and the // config remains the default. - updateConfigAndZenModeLocked(mConfig, "init", true /*setRingerMode*/, - Process.SYSTEM_UID /* callingUid */, true /* is system */, - false /* no broadcasts*/); + updateConfigAndZenModeLocked(mConfig, UPDATE_ORIGIN_INIT, "init", + true /*setRingerMode*/, Process.SYSTEM_UID /* callingUid */); } } @@ -297,24 +285,20 @@ public class ZenModeHelper { /** * Set the {@link DeviceEffectsApplier} used to apply the consolidated effects. * - * <p>If effects were calculated previously (for example, when we loaded a {@link ZenModeConfig} - * that includes activated rules), they will be applied immediately. + * <p>Previously calculated effects (as loaded from the user's {@link ZenModeConfig}) will be + * applied immediately. */ void setDeviceEffectsApplier(@NonNull DeviceEffectsApplier deviceEffectsApplier) { if (!Flags.modesApi()) { return; } - ZenDeviceEffects consolidatedDeviceEffects; synchronized (mConfigLock) { if (mDeviceEffectsApplier != null) { throw new IllegalStateException("Already set up a DeviceEffectsApplier!"); } mDeviceEffectsApplier = deviceEffectsApplier; - consolidatedDeviceEffects = mConsolidatedDeviceEffects; - } - if (consolidatedDeviceEffects.hasEffects()) { - applyConsolidatedDeviceEffects(); } + applyConsolidatedDeviceEffects(UPDATE_ORIGIN_INIT); } public void onUserSwitched(int user) { @@ -353,7 +337,8 @@ public class ZenModeHelper { config.user = user; } synchronized (mConfigLock) { - setConfigLocked(config, null, reason, Process.SYSTEM_UID, true); + setConfigLocked(config, null, UPDATE_ORIGIN_INIT_USER, reason, + Process.SYSTEM_UID); } cleanUpZenRules(); } @@ -366,9 +351,11 @@ public class ZenModeHelper { boolean fromSystemOrSystemUi) { final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); if (newZen != -1) { - setManualZenMode(newZen, null, name != null ? name.getPackageName() : null, - "listener:" + (name != null ? name.flattenToShortString() : null), - callingUid, fromSystemOrSystemUi); + setManualZenMode(newZen, null, + fromSystemOrSystemUi ? UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI : UPDATE_ORIGIN_APP, + /* reason= */ "listener:" + (name != null ? name.flattenToShortString() : null), + /* caller= */ name != null ? name.getPackageName() : null, + callingUid); } } @@ -428,7 +415,7 @@ public class ZenModeHelper { } public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, - String reason, int callingUid, @ChangeOrigin int origin) { + @ConfigChangeOrigin int origin, String reason, int callingUid) { if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { @@ -462,10 +449,9 @@ public class ZenModeHelper { } newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); - populateZenRule(pkg, automaticZenRule, rule, true, origin); + populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true); newConfig.automaticRules.put(rule.id, rule); - if (setConfigLocked(newConfig, reason, rule.component, true, callingUid, - origin == FROM_SYSTEM_OR_SYSTEMUI)) { + if (setConfigLocked(newConfig, origin, reason, rule.component, true, callingUid)) { return rule.id; } else { throw new AndroidRuntimeException("Could not create rule"); @@ -474,7 +460,7 @@ public class ZenModeHelper { } public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule, - String reason, int callingUid, @ChangeOrigin int origin) { + @ConfigChangeOrigin int origin, String reason, int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return false; @@ -502,9 +488,9 @@ public class ZenModeHelper { } } - populateZenRule(rule.pkg, automaticZenRule, rule, false, origin); - return setConfigLocked(newConfig, reason, rule.component, true, callingUid, - origin == FROM_SYSTEM_OR_SYSTEMUI); + populateZenRule(rule.pkg, automaticZenRule, rule, origin, /* isNew= */ false); + return setConfigLocked(newConfig, origin, reason, + rule.component, true, callingUid); } } @@ -541,8 +527,7 @@ public class ZenModeHelper { Condition deactivated = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_deactivated), Condition.STATE_FALSE); - setAutomaticZenRuleState(rule.id, deactivated, - callingUid, /* fromSystemOrSystemUi= */ false); + setAutomaticZenRuleState(rule.id, deactivated, UPDATE_ORIGIN_APP, callingUid); } } else { // Either create a new rule with a default ZenPolicy, or update an existing rule's @@ -558,9 +543,8 @@ public class ZenModeHelper { rule.condition = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_activated), Condition.STATE_TRUE); - setConfigLocked(newConfig, /* triggeringComponent= */ null, - "applyGlobalZenModeAsImplicitZenRule", - callingUid, /* fromSystemOrSystemUi= */ false); + setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP, + "applyGlobalZenModeAsImplicitZenRule", callingUid); } } } @@ -595,9 +579,8 @@ public class ZenModeHelper { } // TODO: b/308673679 - Keep user customization of this rule! rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy); - setConfigLocked(newConfig, /* triggeringComponent= */ null, - "applyGlobalPolicyAsImplicitZenRule", - callingUid, /* fromSystemOrSystemUi= */ false); + setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP, + "applyGlobalPolicyAsImplicitZenRule", callingUid); } } @@ -669,8 +652,8 @@ public class ZenModeHelper { return "implicit_" + forPackage; } - public boolean removeAutomaticZenRule(String id, String reason, int callingUid, - boolean fromSystemOrSystemUi) { + boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason, + int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return false; @@ -695,13 +678,12 @@ public class ZenModeHelper { } dispatchOnAutomaticRuleStatusChanged( mConfig.user, ruleToRemove.getPkg(), id, AUTOMATIC_RULE_STATUS_REMOVED); - return setConfigLocked(newConfig, reason, null, true, callingUid, - fromSystemOrSystemUi); + return setConfigLocked(newConfig, origin, reason, null, true, callingUid); } } - public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid, - boolean fromSystemOrSystemUi) { + boolean removeAutomaticZenRules(String packageName, @ConfigChangeOrigin int origin, + String reason, int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return false; @@ -712,13 +694,12 @@ public class ZenModeHelper { newConfig.automaticRules.removeAt(i); } } - return setConfigLocked(newConfig, reason, null, true, callingUid, - fromSystemOrSystemUi); + return setConfigLocked(newConfig, origin, reason, null, true, callingUid); } } - public void setAutomaticZenRuleState(String id, Condition condition, int callingUid, - boolean fromSystemOrSystemUi) { + void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin, + int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return; @@ -726,13 +707,12 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ArrayList<ZenRule> rules = new ArrayList<>(); rules.add(newConfig.automaticRules.get(id)); - setAutomaticZenRuleStateLocked(newConfig, rules, condition, callingUid, - fromSystemOrSystemUi); + setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid); } } - public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid, - boolean fromSystemOrSystemUi) { + void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, + @ConfigChangeOrigin int origin, int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return; @@ -740,20 +720,23 @@ public class ZenModeHelper { setAutomaticZenRuleStateLocked(newConfig, findMatchingRules(newConfig, ruleDefinition, condition), - condition, callingUid, fromSystemOrSystemUi); + condition, origin, callingUid); } } @GuardedBy("mConfigLock") private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules, - Condition condition, int callingUid, boolean fromSystemOrSystemUi) { + Condition condition, @ConfigChangeOrigin int origin, int callingUid) { if (rules == null || rules.isEmpty()) return; + if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) { + origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action. + } + for (ZenRule rule : rules) { rule.condition = condition; updateSnoozing(rule); - setConfigLocked(config, rule.component, "conditionChanged", callingUid, - fromSystemOrSystemUi); + setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid); } } @@ -857,7 +840,7 @@ public class ZenModeHelper { // update default rule (if locale changed, name of rule will change) currRule.name = defaultRule.name; updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule), - "locale changed", callingUid, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "locale changed", callingUid); } } } @@ -899,13 +882,12 @@ public class ZenModeHelper { return null; } - @VisibleForTesting void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule, - boolean isNew, @ChangeOrigin int origin) { - // TODO: b/308671593,b/311406021 - Handle origins more precisely: - // - FROM_USER can override anything and updates bitmask of user-modified fields; - // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask; - // - FROM_APP can only update if not user-modified. + @ConfigChangeOrigin int origin, boolean isNew) { + // TODO: b/308671593,b/311406021 - Handle origins more precisely: + // - USER can override anything and updates bitmask of user-modified fields; + // - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask; + // - APP can only update if not user-modified. if (rule.enabled != automaticZenRule.isEnabled()) { rule.snoozing = false; } @@ -951,12 +933,12 @@ public class ZenModeHelper { */ @Nullable private static ZenDeviceEffects fixZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects, - @Nullable ZenDeviceEffects newEffects, @ChangeOrigin int origin) { + @Nullable ZenDeviceEffects newEffects, @ConfigChangeOrigin int origin) { // TODO: b/308671593,b/311406021 - Handle origins more precisely: - // - FROM_USER can override anything and updates bitmask of user-modified fields; - // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask; - // - FROM_APP can only update if not user-modified. - if (origin == FROM_SYSTEM_OR_SYSTEMUI || origin == FROM_USER) { + // - USER can override anything and updates bitmask of user-modified fields; + // - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask; + // - APP can only update if not user-modified. + if (origin != UPDATE_ORIGIN_APP) { return newEffects; } @@ -1033,16 +1015,16 @@ public class ZenModeHelper { : AUTOMATIC_RULE_STATUS_DISABLED); } - public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason, - int callingUid, boolean fromSystemOrSystemUi) { - setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/, callingUid, - fromSystemOrSystemUi); + void setManualZenMode(int zenMode, Uri conditionId, @ConfigChangeOrigin int origin, + String reason, @Nullable String caller, int callingUid) { + setManualZenMode(zenMode, conditionId, origin, reason, caller, true /*setRingerMode*/, + callingUid); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0); } - private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller, - boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) { + private void setManualZenMode(int zenMode, Uri conditionId, @ConfigChangeOrigin int origin, + String reason, @Nullable String caller, boolean setRingerMode, int callingUid) { ZenModeConfig newConfig; synchronized (mConfigLock) { if (mConfig == null) return; @@ -1069,8 +1051,7 @@ public class ZenModeHelper { } newConfig.manualRule = newRule; } - setConfigLocked(newConfig, reason, null, setRingerMode, callingUid, - fromSystemOrSystemUi); + setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid); } } @@ -1199,7 +1180,9 @@ public class ZenModeHelper { } if (DEBUG) Log.d(TAG, reason); synchronized (mConfigLock) { - setConfigLocked(config, null, reason, Process.SYSTEM_UID, true); + setConfigLocked(config, null, + forRestore ? UPDATE_ORIGIN_RESTORE_BACKUP : UPDATE_ORIGIN_INIT, reason, + Process.SYSTEM_UID); } } } @@ -1233,13 +1216,13 @@ public class ZenModeHelper { /** * Sets the global notification policy used for priority only do not disturb */ - public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) { + public void setNotificationPolicy(Policy policy, @ConfigChangeOrigin int origin, + int callingUid) { synchronized (mConfigLock) { if (policy == null || mConfig == null) return; final ZenModeConfig newConfig = mConfig.copy(); newConfig.applyNotificationPolicy(policy); - setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid, - fromSystemOrSystemUi); + setConfigLocked(newConfig, null, origin, "setNotificationPolicy", callingUid); } } @@ -1264,8 +1247,8 @@ public class ZenModeHelper { } } } - setConfigLocked(newConfig, null, "cleanUpZenRules", Process.SYSTEM_UID, - true); + setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules", + Process.SYSTEM_UID); } } @@ -1287,22 +1270,22 @@ public class ZenModeHelper { @GuardedBy("mConfigLock") private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent, - String reason, int callingUid, boolean fromSystemOrSystemUi) { - return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/, - callingUid, fromSystemOrSystemUi); + @ConfigChangeOrigin int origin, String reason, int callingUid) { + return setConfigLocked(config, origin, reason, triggeringComponent, true /*setRingerMode*/, + callingUid); } - public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason, - int callingUid, boolean fromSystemOrSystemUi) { + void setConfig(ZenModeConfig config, ComponentName triggeringComponent, + @ConfigChangeOrigin int origin, String reason, int callingUid) { synchronized (mConfigLock) { - setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi); + setConfigLocked(config, triggeringComponent, origin, reason, callingUid); } } @GuardedBy("mConfigLock") - private boolean setConfigLocked(ZenModeConfig config, String reason, - ComponentName triggeringComponent, boolean setRingerMode, int callingUid, - boolean fromSystemOrSystemUi) { + private boolean setConfigLocked(ZenModeConfig config, @ConfigChangeOrigin int origin, + String reason, ComponentName triggeringComponent, boolean setRingerMode, + int callingUid) { final long identity = Binder.clearCallingIdentity(); try { if (config == null || !config.isValid()) { @@ -1332,8 +1315,7 @@ public class ZenModeHelper { if (policyChanged) { dispatchOnPolicyChanged(); } - updateConfigAndZenModeLocked(config, reason, setRingerMode, callingUid, - fromSystemOrSystemUi, true); + updateConfigAndZenModeLocked(config, origin, reason, setRingerMode, callingUid); mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/); return true; } catch (SecurityException e) { @@ -1349,17 +1331,16 @@ public class ZenModeHelper { * If logging is enabled, will also request logging of the outcome of this change if needed. */ @GuardedBy("mConfigLock") - private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason, - boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi, - boolean sendBroadcasts) { + private void updateConfigAndZenModeLocked(ZenModeConfig config, @ConfigChangeOrigin int origin, + String reason, boolean setRingerMode, int callingUid) { final boolean logZenModeEvents = mFlagResolver.isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS); // Store (a copy of) all config and zen mode info prior to any changes taking effect ZenModeEventLogger.ZenModeInfo prevInfo = new ZenModeEventLogger.ZenModeInfo( mZenMode, mConfig, mConsolidatedPolicy); if (!config.equals(mConfig)) { - // schedule broadcasts - if (Flags.modesApi() && sendBroadcasts) { + // Schedule broadcasts. Cannot be sent during boot, though. + if (Flags.modesApi() && origin != UPDATE_ORIGIN_INIT) { for (ZenRule rule : config.automaticRules.values()) { ZenRule original = mConfig.automaticRules.get(rule.id); if (original != null) { @@ -1377,15 +1358,19 @@ public class ZenModeHelper { mConfig = config; dispatchOnConfigChanged(); - updateAndApplyConsolidatedPolicyAndDeviceEffects(reason); + updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason); } final String val = Integer.toString(config.hashCode()); Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); - evaluateZenModeLocked(reason, setRingerMode); + evaluateZenModeLocked(origin, reason, setRingerMode); // After all changes have occurred, log if requested if (logZenModeEvents) { ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo( mZenMode, mConfig, mConsolidatedPolicy); + boolean fromSystemOrSystemUi = origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI + || origin == UPDATE_ORIGIN_INIT + || origin == UPDATE_ORIGIN_INIT_USER + || origin == UPDATE_ORIGIN_RESTORE_BACKUP; mZenModeEventLogger.maybeLogZenChange(prevInfo, newInfo, callingUid, fromSystemOrSystemUi); } @@ -1416,7 +1401,8 @@ public class ZenModeHelper { @VisibleForTesting @GuardedBy("mConfigLock") - protected void evaluateZenModeLocked(String reason, boolean setRingerMode) { + protected void evaluateZenModeLocked(@ConfigChangeOrigin int origin, String reason, + boolean setRingerMode) { if (DEBUG) Log.d(TAG, "evaluateZenMode"); if (mConfig == null) return; final int policyHashBefore = mConsolidatedPolicy == null ? 0 @@ -1426,7 +1412,7 @@ public class ZenModeHelper { ZenLog.traceSetZenMode(zen, reason); mZenMode = zen; setZenModeSetting(mZenMode); - updateAndApplyConsolidatedPolicyAndDeviceEffects(reason); + updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason); boolean shouldApplyToRinger = setRingerMode && (zen != zenBefore || ( zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS && policyHashBefore != mConsolidatedPolicy.hashCode())); @@ -1468,6 +1454,7 @@ public class ZenModeHelper { } } + @GuardedBy("mConfigLock") private void applyCustomPolicy(ZenPolicy policy, ZenRule rule) { if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) { policy.apply(new ZenPolicy.Builder() @@ -1487,7 +1474,9 @@ public class ZenModeHelper { } } - private void updateAndApplyConsolidatedPolicyAndDeviceEffects(String reason) { + @GuardedBy("mConfigLock") + private void updateAndApplyConsolidatedPolicyAndDeviceEffects(@ConfigChangeOrigin int origin, + String reason) { synchronized (mConfigLock) { if (mConfig == null) return; ZenPolicy policy = new ZenPolicy(); @@ -1519,13 +1508,13 @@ public class ZenModeHelper { ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build(); if (!deviceEffects.equals(mConsolidatedDeviceEffects)) { mConsolidatedDeviceEffects = deviceEffects; - mHandler.postApplyDeviceEffects(); + mHandler.postApplyDeviceEffects(origin); } } } } - private void applyConsolidatedDeviceEffects() { + private void applyConsolidatedDeviceEffects(@ConfigChangeOrigin int source) { if (!Flags.modesApi()) { return; } @@ -1536,7 +1525,7 @@ public class ZenModeHelper { effects = mConsolidatedDeviceEffects; } if (applier != null) { - applier.apply(effects); + applier.apply(effects, source); } } @@ -1801,8 +1790,8 @@ public class ZenModeHelper { } @Override - public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller, - int ringerModeExternal, VolumePolicy policy) { + public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, + @Nullable String caller, int ringerModeExternal, VolumePolicy policy) { final boolean isChange = ringerModeOld != ringerModeNew; int ringerModeExternalOut = ringerModeNew; @@ -1839,8 +1828,9 @@ public class ZenModeHelper { } if (newZen != -1) { - setManualZenMode(newZen, null, "ringerModeInternal", null, - false /*setRingerMode*/, Process.SYSTEM_UID, true); + setManualZenMode(newZen, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "ringerModeInternal", /* caller= */ null, /* setRingerMode= */ false, + Process.SYSTEM_UID); } if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) { ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller, @@ -1856,8 +1846,8 @@ public class ZenModeHelper { } @Override - public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller, - int ringerModeInternal, VolumePolicy policy) { + public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, + @Nullable String caller, int ringerModeInternal, VolumePolicy policy) { int ringerModeInternalOut = ringerModeNew; final boolean isChange = ringerModeOld != ringerModeNew; final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; @@ -1883,8 +1873,8 @@ public class ZenModeHelper { break; } if (newZen != -1) { - setManualZenMode(newZen, null, "ringerModeExternal", caller, - false /*setRingerMode*/, Process.SYSTEM_UID, true); + setManualZenMode(newZen, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "ringerModeExternal", caller, false /*setRingerMode*/, Process.SYSTEM_UID); } ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller, @@ -2024,11 +2014,9 @@ public class ZenModeHelper { } try { final Resources res = mPm.getResourcesForApplication(packageName); - final String fullName = res.getResourceName(resId); - - return fullName; + return res.getResourceName(resId); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { - Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName + Slog.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName + ". Resource IDs may change when the application is upgraded, and the system" + " may not be able to find the correct resource."); return null; @@ -2149,9 +2137,9 @@ public class ZenModeHelper { sendMessage(obtainMessage(MSG_RINGER_AUDIO, shouldApplyToRinger)); } - private void postApplyDeviceEffects() { + private void postApplyDeviceEffects(@ConfigChangeOrigin int origin) { removeMessages(MSG_APPLY_EFFECTS); - sendEmptyMessage(MSG_APPLY_EFFECTS); + sendMessage(obtainMessage(MSG_APPLY_EFFECTS, origin, 0)); } @Override @@ -2168,7 +2156,8 @@ public class ZenModeHelper { updateRingerAndAudio(shouldApplyToRinger); break; case MSG_APPLY_EFFECTS: - applyConsolidatedDeviceEffects(); + @ConfigChangeOrigin int origin = msg.arg1; + applyConsolidatedDeviceEffects(origin); break; } } diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index 167c17cc0c80..79d17534ab26 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; @@ -25,7 +26,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.os.CreateAppDataArgs; import android.os.Environment; import android.os.FileUtils; @@ -49,7 +49,6 @@ import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.ArtManagerService; 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.SELinuxUtil; @@ -57,6 +56,7 @@ import dalvik.system.VMRuntime; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -93,25 +93,42 @@ public class AppDataHelper { */ @GuardedBy("mPm.mInstallLock") public void prepareAppDataAfterInstallLIF(AndroidPackage pkg) { - prepareAppDataPostCommitLIF(pkg, 0 /* previousAppId */); + final PackageSetting ps; + synchronized (mPm.mLock) { + ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + } + + prepareAppDataPostCommitLIF(ps, 0 /* previousAppId */, getInstalledUsersForPackage(ps)); + } + + private int[] getInstalledUsersForPackage(PackageSetting ps) { + UserManagerInternal umInternal = mInjector.getUserManagerInternal(); + var users = umInternal.getUsers(false /*excludeDying*/); + int[] userIds = new int[users.size()]; + int userIdsCount = 0; + for (int i = 0, size = users.size(); i < size; ++i) { + int userId = users.get(i).id; + if (ps.getInstalled(userId)) { + userIds[userIdsCount++] = userId; + } + } + return Arrays.copyOf(userIds, userIdsCount); } /** * For more details about data verification and previousAppId, check - * {@link #prepareAppData(Installer.Batch, AndroidPackage, int, int, int)} - * @see #prepareAppDataAfterInstallLIF(AndroidPackage) + * {@link #prepareAppData} + * @see #prepareAppDataAfterInstallLIF */ @GuardedBy("mPm.mInstallLock") - public void prepareAppDataPostCommitLIF(AndroidPackage pkg, int previousAppId) { - final PackageSetting ps; + public void prepareAppDataPostCommitLIF(PackageSetting ps, int previousAppId, int[] userIds) { synchronized (mPm.mLock) { - ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); mPm.mSettings.writeKernelMappingLPr(ps); } // TODO(b/211761016): should we still create the profile dirs? - if (!shouldHaveAppStorage(pkg)) { - Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName()); + if (ps.getPkg() != null && !shouldHaveAppStorage(ps.getPkg())) { + Slog.w(TAG, "Skipping preparing app data for " + ps.getPackageName()); return; } @@ -119,31 +136,29 @@ public class AppDataHelper { UserManagerInternal umInternal = mInjector.getUserManagerInternal(); StorageManagerInternal smInternal = mInjector.getLocalService( StorageManagerInternal.class); - for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) { + for (int userId : userIds) { final int flags; - if (StorageManager.isCeStorageUnlocked(user.id) - && smInternal.isCeStoragePrepared(user.id)) { + if (StorageManager.isCeStorageUnlocked(userId) + && smInternal.isCeStoragePrepared(userId)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; - } else if (umInternal.isUserRunning(user.id)) { + } else if (umInternal.isUserRunning(userId)) { flags = StorageManager.FLAG_STORAGE_DE; } else { continue; } - if (ps.getInstalled(user.id)) { - // TODO: when user data is locked, mark that we're still dirty - prepareAppData(batch, pkg, previousAppId, user.id, flags).thenRun(() -> { - // Note: this code block is executed with the Installer lock - // already held, since it's invoked as a side-effect of - // executeBatchLI() - if (umInternal.isUserUnlockingOrUnlocked(user.id)) { - // Prepare app data on external storage; currently this is used to - // setup any OBB dirs that were created by the installer correctly. - int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid())); - smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid); - } - }); - } + // TODO: when user data is locked, mark that we're still dirty + prepareAppData(batch, ps, previousAppId, userId, flags).thenRun(() -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (umInternal.isUserUnlockingOrUnlocked(userId)) { + // Prepare app data on external storage; currently this is used to + // setup any OBB dirs that were created by the installer correctly. + int uid = UserHandle.getUid(userId, ps.getAppId()); + smInternal.prepareAppDataAfterInstall(ps.getPackageName(), uid); + } + }); } executeBatchLI(batch); } @@ -156,73 +171,70 @@ public class AppDataHelper { } } - /** - * Prepare app data for the given app. - * <p> - * Verifies that directories exist and that ownership and labeling is - * correct for all installed apps. If there is an ownership mismatch: - * <ul> - * <li>If previousAppId < 0, app data will be migrated to the new app ID - * <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated - * <li>If previousAppId > 0, app data owned by previousAppId will be migrated to the new app ID - * </ul> - */ - private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch, - @Nullable AndroidPackage pkg, int previousAppId, int userId, - @StorageManager.StorageFlags int flags) { + private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch, + @NonNull AndroidPackage pkg, @UserIdInt int userId, + @StorageManager.StorageFlags int flags, boolean maybeMigrateAppData) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); - return CompletableFuture.completedFuture(null); + return; } if (!shouldHaveAppStorage(pkg)) { Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName()); - return CompletableFuture.completedFuture(null); + return; } - return prepareAppDataLeaf(batch, pkg, previousAppId, userId, flags); - } - - private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch, - @NonNull PackageState packageState, @NonNull AndroidPackage pkg, @UserIdInt int userId, - @StorageManager.StorageFlags int flags, boolean maybeMigrateAppData) { - prepareAppData(batch, pkg, Process.INVALID_UID, userId, flags).thenRun(() -> { + final PackageSetting ps; + synchronized (mPm.mLock) { + ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + } + prepareAppData(batch, ps, Process.INVALID_UID, userId, flags).thenRun(() -> { // Note: this code block is executed with the Installer lock // already held, since it's invoked as a side-effect of // executeBatchLI() - if (maybeMigrateAppData && maybeMigrateAppDataLIF(packageState, pkg, userId)) { + if (maybeMigrateAppData && maybeMigrateAppDataLIF(ps, userId)) { // We may have just shuffled around app data directories, so // prepare them one more time final Installer.Batch batchInner = new Installer.Batch(); - prepareAppData(batchInner, pkg, Process.INVALID_UID, userId, flags); + prepareAppData(batchInner, ps, Process.INVALID_UID, userId, flags); executeBatchLI(batchInner); } }); } - private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch, - @NonNull AndroidPackage pkg, int previousAppId, int userId, int flags) { + /** + * Prepare app data for the given app. + * <p> + * Verifies that directories exist and that ownership and labeling is + * correct for all installed apps. If there is an ownership mismatch: + * <ul> + * <li>If previousAppId < 0, app data will be migrated to the new app ID + * <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated + * <li>If previousAppId > 0, app data owned by previousAppId will be migrated to the new app ID + * </ul> + */ + private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch, + @NonNull PackageSetting ps, int previousAppId, int userId, int flags) { + final String packageName = ps.getPackageName(); + if (DEBUG_APP_DATA) { - Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x" + Slog.v(TAG, "prepareAppData for " + packageName + " u" + userId + " 0x" + Integer.toHexString(flags)); } - final PackageSetting ps; final String seInfoUser; synchronized (mPm.mLock) { - ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); seInfoUser = SELinuxUtil.getSeinfoUser(ps.readUserState(userId)); } - final String volumeUuid = pkg.getVolumeUuid(); - final String packageName = pkg.getPackageName(); - final int appId = UserHandle.getAppId(pkg.getUid()); + final AndroidPackage pkg = ps.getPkg(); + final String volumeUuid = ps.getVolumeUuid(); + final int appId = ps.getAppId(); String pkgSeInfo = ps.getSeInfo(); - Preconditions.checkNotNull(pkgSeInfo); final String seInfo = pkgSeInfo + seInfoUser; - final int targetSdkVersion = pkg.getTargetSdkVersion(); - final boolean usesSdk = !pkg.getUsesSdkLibraries().isEmpty(); + final int targetSdkVersion = ps.getTargetSdkVersion(); + final boolean usesSdk = ps.getUsesSdkLibraries().length > 0; final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(volumeUuid, packageName, userId, flags, appId, seInfo, targetSdkVersion, usesSdk); args.previousAppId = previousAppId; @@ -234,7 +246,7 @@ public class AppDataHelper { if (e != null) { logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName + ", but trying to recover: " + e); - destroyAppDataLeafLIF(pkg, userId, flags); + destroyAppDataLeafLIF(packageName, volumeUuid, userId, flags); try { createAppDataResult = mInstaller.createAppData(args); logCriticalInfo(Log.DEBUG, "Recovery succeeded!"); @@ -267,8 +279,8 @@ public class AppDataHelper { // #performDexOptUpgrade. When we do that we should have a // more granular check here and only update the existing // profiles. - if (mPm.isDeviceUpgrading() || mPm.isFirstBoot() - || (userId != UserHandle.USER_SYSTEM)) { + if (pkg != null && (mPm.isDeviceUpgrading() || mPm.isFirstBoot() + || (userId != UserHandle.USER_SYSTEM))) { try { mArtManagerService.prepareAppProfiles(pkg, userId, /* updateReferenceProfileContent= */ false); @@ -292,7 +304,9 @@ public class AppDataHelper { } } - prepareAppDataContentsLeafLIF(pkg, ps, userId, flags); + if (pkg != null) { + prepareAppDataContentsLeafLIF(pkg, ps, userId, flags); + } }); } @@ -336,18 +350,17 @@ public class AppDataHelper { * CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag * requested by the app. */ - private boolean maybeMigrateAppDataLIF(@NonNull PackageState packageState, - @NonNull AndroidPackage pkg, @UserIdInt int userId) { - if (packageState.isSystem() && !StorageManager.isFileEncrypted() + private boolean maybeMigrateAppDataLIF(@NonNull PackageSetting ps, @UserIdInt int userId) { + if (ps.isSystem() && !StorageManager.isFileEncrypted() && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { - final int storageTarget = pkg.isDefaultToDeviceProtectedStorage() + final int storageTarget = ps.isDefaultToDeviceProtectedStorage() ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE; try { - mInstaller.migrateAppData(pkg.getVolumeUuid(), pkg.getPackageName(), userId, + mInstaller.migrateAppData(ps.getVolumeUuid(), ps.getPackageName(), userId, storageTarget); } catch (Installer.InstallerException e) { logCriticalInfo(Log.WARN, - "Failed to migrate " + pkg.getPackageName() + ": " + e.getMessage()); + "Failed to migrate " + ps.getPackageName() + ": " + e.getMessage()); } return true; } else { @@ -471,7 +484,7 @@ public class AppDataHelper { } if (ps.getUserStateOrDefault(userId).isInstalled()) { - prepareAppDataAndMigrate(batch, ps, ps.getPkg(), userId, flags, migrateAppData); + prepareAppDataAndMigrate(batch, ps.getPkg(), userId, flags, migrateAppData); preparedCount++; } } @@ -550,7 +563,7 @@ public class AppDataHelper { && packageStateInternal.getUserStateOrDefault( UserHandle.USER_SYSTEM).isInstalled()) { AndroidPackage pkg = packageStateInternal.getPkg(); - prepareAppDataAndMigrate(batch, packageStateInternal, pkg, + prepareAppDataAndMigrate(batch, pkg, UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */); count++; } @@ -568,22 +581,22 @@ public class AppDataHelper { if (pkg == null) { return; } - clearAppDataLeafLIF(pkg, userId, flags); + clearAppDataLeafLIF(pkg.getPackageName(), pkg.getVolumeUuid(), userId, flags); if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) { clearAppProfilesLIF(pkg); } } - private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { + void clearAppDataLeafLIF(String packageName, String volumeUuid, int userId, int flags) { final Computer snapshot = mPm.snapshotComputer(); final PackageStateInternal packageStateInternal = - snapshot.getPackageStateInternal(pkg.getPackageName()); + snapshot.getPackageStateInternal(packageName); for (int realUserId : mPm.resolveUserIds(userId)) { final long ceDataInode = (packageStateInternal != null) ? packageStateInternal.getUserStateOrDefault(realUserId).getCeDataInode() : 0; try { - mInstaller.clearAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId, + mInstaller.clearAppData(volumeUuid, packageName, realUserId, flags, ceDataInode); } catch (Installer.InstallerException e) { Slog.w(TAG, String.valueOf(e)); @@ -597,7 +610,7 @@ public class AppDataHelper { return; } if (DexOptHelper.useArtService()) { - destroyAppProfilesWithArtService(pkg); + destroyAppProfilesWithArtService(pkg.getPackageName()); } else { try { mArtManagerService.clearAppProfiles(pkg); @@ -612,41 +625,37 @@ public class AppDataHelper { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } - destroyAppDataLeafLIF(pkg, userId, flags); + destroyAppDataLeafLIF(pkg.getPackageName(), pkg.getVolumeUuid(), userId, flags); } - private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) { + private void destroyAppDataLeafLIF( + String packageName, String volumeUuid, int userId, int flags) { final Computer snapshot = mPm.snapshotComputer(); final PackageStateInternal packageStateInternal = - snapshot.getPackageStateInternal(pkg.getPackageName()); + snapshot.getPackageStateInternal(packageName); for (int realUserId : mPm.resolveUserIds(userId)) { final long ceDataInode = (packageStateInternal != null) ? packageStateInternal.getUserStateOrDefault(realUserId).getCeDataInode() : 0; try { - mInstaller.destroyAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId, + mInstaller.destroyAppData(volumeUuid, packageName, realUserId, flags, ceDataInode); } catch (Installer.InstallerException e) { Slog.w(TAG, String.valueOf(e)); } - mPm.getDexManager().notifyPackageDataDestroyed(pkg.getPackageName(), userId); - mPm.getDynamicCodeLogger().notifyPackageDataDestroyed(pkg.getPackageName(), userId); + mPm.getDexManager().notifyPackageDataDestroyed(packageName, userId); + mPm.getDynamicCodeLogger().notifyPackageDataDestroyed(packageName, userId); } } - public void destroyAppProfilesLIF(AndroidPackage pkg) { - if (pkg == null) { - Slog.wtf(TAG, "Package was null!", new Throwable()); - return; - } - destroyAppProfilesLeafLIF(pkg); - } - - private void destroyAppProfilesLeafLIF(AndroidPackage pkg) { + /** + * Destroy ART app profiles for the package. + */ + void destroyAppProfilesLIF(String packageName) { if (DexOptHelper.useArtService()) { - destroyAppProfilesWithArtService(pkg); + destroyAppProfilesWithArtService(packageName); } else { try { - mInstaller.destroyAppProfiles(pkg.getPackageName()); + mInstaller.destroyAppProfiles(packageName); } catch (LegacyDexoptDisabledException e) { throw new RuntimeException(e); } catch (Installer.InstallerException e) { @@ -655,7 +664,7 @@ public class AppDataHelper { } } - private void destroyAppProfilesWithArtService(AndroidPackage pkg) { + private void destroyAppProfilesWithArtService(String packageName) { if (!DexOptHelper.artManagerLocalIsInitialized()) { // This function may get called while PackageManagerService is constructed (via e.g. // InitAppsHelper.initSystemApps), and ART Service hasn't yet been started then (it @@ -668,7 +677,7 @@ public class AppDataHelper { try (PackageManagerLocal.FilteredSnapshot snapshot = getPackageManagerLocal().withFilteredSnapshot()) { try { - DexOptHelper.getArtManagerLocal().clearAppProfiles(snapshot, pkg.getPackageName()); + DexOptHelper.getArtManagerLocal().clearAppProfiles(snapshot, packageName); } catch (IllegalArgumentException e) { // Package isn't found, but that should only happen due to race. Slog.w(TAG, e); diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index bdcec3a33221..82622d9a4ea8 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -54,6 +54,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.SparseSetArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -1030,14 +1031,18 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, private void recomputeComponentVisibility( ArrayMap<String, ? extends PackageStateInternal> existingSettings) { final WatchedArraySet<String> protectedBroadcasts; + final WatchedArraySet<Integer> forceQueryable; synchronized (mProtectedBroadcastsLock) { protectedBroadcasts = mProtectedBroadcasts.snapshot(); } + synchronized (mForceQueryableLock) { + forceQueryable = mForceQueryable.snapshot(); + } final ParallelComputeComponentVisibility computer = new ParallelComputeComponentVisibility( - existingSettings, mForceQueryable, protectedBroadcasts); + existingSettings, forceQueryable, protectedBroadcasts); + SparseSetArray<Integer> queriesViaComponent = computer.execute(); synchronized (mQueriesViaComponentLock) { - mQueriesViaComponent.clear(); - computer.execute(mQueriesViaComponent); + mQueriesViaComponent.copyFrom(queriesViaComponent); } mQueriesViaComponentRequireRecompute.set(false); diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java index f3f64c5010ee..200734b37269 100644 --- a/services/core/java/com/android/server/pm/AppsFilterUtils.java +++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java @@ -26,6 +26,7 @@ import android.content.IntentFilter; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; +import android.util.SparseSetArray; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; @@ -37,7 +38,6 @@ 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.utils.WatchedArraySet; -import com.android.server.utils.WatchedSparseSetArray; import java.util.ArrayList; import java.util.List; @@ -213,7 +213,9 @@ final class AppsFilterUtils { /** * Computes component visibility of all packages in parallel from a thread pool. */ - void execute(@NonNull WatchedSparseSetArray<Integer> outQueriesViaComponent) { + @NonNull + SparseSetArray<Integer> execute() { + final SparseSetArray<Integer> queriesViaComponent = new SparseSetArray<>(); final ExecutorService pool = ConcurrentUtils.newFixedThreadPool( MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(), THREAD_PRIORITY_DEFAULT); @@ -239,7 +241,7 @@ final class AppsFilterUtils { try { final ArraySet<Integer> visibleList = future.get(); if (visibleList.size() != 0) { - outQueriesViaComponent.addAll(appId, visibleList); + queriesViaComponent.addAll(appId, visibleList); } } catch (InterruptedException | ExecutionException e) { throw new IllegalStateException(e); @@ -248,6 +250,7 @@ final class AppsFilterUtils { } finally { pool.shutdownNow(); } + return queriesViaComponent; } /** diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 92d469ccbfac..c36c8caf8b19 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -405,9 +405,19 @@ public interface Computer extends PackageDataSnapshot { boolean isInstallDisabledForPackage(@NonNull String packageName, int uid, @UserIdInt int userId); - @Nullable - List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo, - @PackageManager.PackageInfoFlagsBits long flags, int callingUid, @UserIdInt int userId); + /** + * Returns a Pair that contains a list of packages that depend on the target library and the + * package library dependency information. The List<VersionedPackage> indicates a list of + * packages that depend on the target library, it may be null if no package depends on + * the target library. The List<Boolean> indicates whether each VersionedPackage in + * the List<VersionedPackage> optionally depends on the target library, where true means + * optional and false means required. It may be null if no package depends on + * the target library or without dependency information, e.g. uses-static-library. + */ + @NonNull + Pair<List<VersionedPackage>, List<Boolean>> getPackagesUsingSharedLibrary( + @NonNull SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags, + int callingUid, @UserIdInt int userId); @Nullable ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries( diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 7c425b8223c8..2ae10051182d 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -895,6 +895,9 @@ public class ComputerEngine implements Computer { @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) { ParsedActivity a = mComponentResolver.getActivity(component); + // Allow to match activities of quarantined packages. + flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS; + if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName()); @@ -1500,14 +1503,15 @@ public class ComputerEngine implements Computer { state.getFirstInstallTimeMillis(), ps.getLastUpdateTime(), installedPermissions, grantedPermissions, state, userId, ps); - if (packageInfo != null) { - packageInfo.packageName = packageInfo.applicationInfo.packageName = - resolveExternalPackageName(p); - return packageInfo; + if (packageInfo == null) { + return null; } - } - // TODO(b/314808978): Set ps.setPkg to null during install-archived. - if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0 + + packageInfo.packageName = packageInfo.applicationInfo.packageName = + resolveExternalPackageName(p); + + return packageInfo; + } else if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0 && PackageUserStateUtils.isAvailable(state, flags)) { PackageInfo pi = new PackageInfo(); pi.packageName = ps.getPackageName(); @@ -1541,8 +1545,9 @@ public class ComputerEngine implements Computer { + ps.getPackageName() + "]. Provides a minimum info."); } return pi; + } else { + return null; } - return null; } public final PackageInfo getPackageInfo(String packageName, @@ -3860,11 +3865,13 @@ public class ComputerEngine implements Computer { Binder.restoreCallingIdentity(identity); } + var usingSharedLibraryPair = + getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId); SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), declaringPackage, - getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId), + usingSharedLibraryPair.first, (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies())), @@ -3933,13 +3940,15 @@ public class ComputerEngine implements Computer { return false; } + @Override - public List<VersionedPackage> getPackagesUsingSharedLibrary(@NonNull SharedLibraryInfo libInfo, - @PackageManager.PackageInfoFlagsBits long flags, int callingUid, - @UserIdInt int userId) { + public Pair<List<VersionedPackage>, List<Boolean>> getPackagesUsingSharedLibrary( + @NonNull SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags, + int callingUid, @UserIdInt int userId) { List<VersionedPackage> versionedPackages = null; final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates(); final int packageCount = packageStates.size(); + List<Boolean> usesLibsOptional = null; for (int i = 0; i < packageCount; i++) { PackageStateInternal ps = packageStates.valueAt(i); if (ps == null) { @@ -3956,12 +3965,15 @@ public class ComputerEngine implements Computer { libInfo.isStatic() ? ps.getUsesStaticLibraries() : ps.getUsesSdkLibraries(); final long[] libsVersions = libInfo.isStatic() ? ps.getUsesStaticLibrariesVersions() : ps.getUsesSdkLibrariesVersionsMajor(); + final boolean[] libsOptional = libInfo.isSdk() + ? ps.getUsesSdkLibrariesOptional() : null; final int libIdx = ArrayUtils.indexOf(libs, libName); if (libIdx < 0) { continue; } if (libsVersions[libIdx] != libInfo.getLongVersion()) { + // Not expected StaticLib/SdkLib version continue; } if (shouldFilterApplication(ps, callingUid, userId)) { @@ -3970,6 +3982,9 @@ public class ComputerEngine implements Computer { if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } + if (usesLibsOptional == null) { + usesLibsOptional = new ArrayList<>(); + } // If the dependent is a static shared lib, use the public package name String dependentPackageName = ps.getPackageName(); if (ps.getPkg() != null && ps.getPkg().isStaticSharedLibrary()) { @@ -3977,6 +3992,7 @@ public class ComputerEngine implements Computer { } versionedPackages.add(new VersionedPackage(dependentPackageName, ps.getVersionCode())); + usesLibsOptional.add(libsOptional != null && libsOptional[libIdx]); } else if (ps.getPkg() != null) { if (ArrayUtils.contains(ps.getPkg().getUsesLibraries(), libName) || ArrayUtils.contains(ps.getPkg().getUsesOptionalLibraries(), libName)) { @@ -3992,7 +4008,7 @@ public class ComputerEngine implements Computer { } } - return versionedPackages; + return new Pair<>(versionedPackages, usesLibsOptional); } @Nullable @@ -4051,13 +4067,14 @@ public class ComputerEngine implements Computer { Binder.restoreCallingIdentity(identity); } + var usingSharedLibraryPair = + getPackagesUsingSharedLibrary(libraryInfo, flags, callingUid, userId); SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( libraryInfo.getPath(), libraryInfo.getPackageName(), libraryInfo.getAllCodePaths(), libraryInfo.getName(), libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), - getPackagesUsingSharedLibrary( - libraryInfo, flags, callingUid, userId), + usingSharedLibraryPair.first, libraryInfo.getDependencies() == null ? null : new ArrayList<>(libraryInfo.getDependencies()), libraryInfo.isNative()); diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 80e6c833dfb2..93836266d1f4 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -18,7 +18,6 @@ package com.android.server.pm; import static android.Manifest.permission.CONTROL_KEYGUARD; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; -import static android.content.pm.Flags.sdkLibIndependence; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; @@ -40,6 +39,7 @@ import android.app.ApplicationExitInfo; import android.app.ApplicationPackageManager; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; import android.content.pm.IPackageDeleteObserver2; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; @@ -72,8 +72,6 @@ import com.android.server.wm.ActivityTaskManagerInternal; import dalvik.system.VMRuntime; -import java.util.List; - /** * Deletes a package. Uninstall if installed, or at least deletes the base directory if it's called * from a failed installation. Fixes user state after deletion. @@ -181,16 +179,36 @@ final class DeletePackageHelper { } if (libraryInfo != null) { + boolean flagSdkLibIndependence = Flags.sdkLibIndependence(); for (int currUserId : allUsers) { if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) { continue; } - List<VersionedPackage> libClientPackages = - computer.getPackagesUsingSharedLibrary(libraryInfo, - MATCH_KNOWN_PACKAGES, Process.SYSTEM_UID, currUserId); - boolean allowSdkLibIndependence = - (pkg.getSdkLibraryName() != null) && sdkLibIndependence(); - if (!ArrayUtils.isEmpty(libClientPackages) && !allowSdkLibIndependence) { + var libClientPackagesPair = computer.getPackagesUsingSharedLibrary( + libraryInfo, MATCH_KNOWN_PACKAGES, Process.SYSTEM_UID, currUserId); + var libClientPackages = libClientPackagesPair.first; + var libClientOptional = libClientPackagesPair.second; + // We by default don't allow removing a package if the host lib is still be + // used by other client packages + boolean allowLibIndependence = false; + // Only when the sdkLibIndependence flag is enabled we will respect the + // "optional" attr in uses-sdk-library. Only allow to remove sdk-lib host + // package if no required clients depend on it + if ((pkg.getSdkLibraryName() != null) + && !ArrayUtils.isEmpty(libClientPackages) + && !ArrayUtils.isEmpty(libClientOptional) + && (libClientPackages.size() == libClientOptional.size()) + && flagSdkLibIndependence) { + allowLibIndependence = true; + for (int i = 0; i < libClientPackages.size(); i++) { + boolean usesSdkLibOptional = libClientOptional.get(i); + if (!usesSdkLibOptional) { + allowLibIndependence = false; + break; + } + } + } + if (!ArrayUtils.isEmpty(libClientPackages) && !allowLibIndependence) { Slog.w(TAG, "Not removing package " + pkg.getManifestPackageName() + " hosting lib " + libraryInfo.getName() + " version " + libraryInfo.getLongVersion() + " used by " + libClientPackages diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 8270481c80f6..83a6f10f0e2a 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -731,7 +731,8 @@ final class InstallPackageHelper { synchronized (mPm.mInstallLock) { // We don't need to freeze for a brand new install - mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg()); + mAppDataHelper.prepareAppDataPostCommitLIF( + pkgSetting, /* previousAppId= */0, new int[] { userId }); } } // TODO(b/278553670) Store archive state for the user. @@ -2432,9 +2433,9 @@ final class InstallPackageHelper { final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0); final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0); final PackageSetting ps = installRequest.getScannedPackageSetting(); + final String packageName = ps.getPackageName(); + final String codePath = ps.getPathString(); final AndroidPackage pkg = ps.getPkg(); - final String packageName = pkg.getPackageName(); - final String codePath = pkg.getPath(); final boolean onIncremental = mIncrementalManager != null && isIncrementalPath(codePath); if (onIncremental) { @@ -2447,18 +2448,19 @@ final class InstallPackageHelper { } // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) - mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0); + mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers()); if (installRequest.isClearCodeCache()) { - mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL, - FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL - | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mAppDataHelper.clearAppDataLeafLIF(packageName, ps.getVolumeUuid(), + UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE + | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } - if (installRequest.isInstallReplace()) { - mDexManager.notifyPackageUpdated(pkg.getPackageName(), + if (installRequest.isInstallReplace() && pkg != null) { + mDexManager.notifyPackageUpdated(packageName, pkg.getBaseApkPath(), pkg.getSplitCodePaths()); } - if (!useArtService()) { // ART Service handles this on demand instead. + // ART Service handles this on demand instead. + if (!useArtService() && pkg != null) { // Prepare the application profiles for the new code paths. // This needs to be done before invoking dexopt so that any install-time profile // can be used for optimizations. @@ -2523,6 +2525,7 @@ final class InstallPackageHelper { (!instantApp || android.provider.Settings.Global.getInt( mContext.getContentResolver(), android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) + && pkg != null && !pkg.isDebuggable() && (!onIncremental) && dexoptOptions.isCompilationEnabled() diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 968ea6688328..2880f84c6445 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1540,6 +1540,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService continue; } pkgSetting + .setPkg(null) .modifyUserState(userId) .setInstalled(false) .setArchiveState(archiveState); @@ -4604,6 +4605,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService userIds, null, broadcastAllowList, null, null); }); + mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED, + packageName, extras, userIds, null /* instantUserIds */, + broadcastAllowList, mHandler); } } } @@ -7109,9 +7113,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (info == null) { continue; } - final List<VersionedPackage> dependents = - computer.getPackagesUsingSharedLibrary(info, 0, Process.SYSTEM_UID, - userId); + var usingSharedLibraryPair = computer.getPackagesUsingSharedLibrary(info, 0, + Process.SYSTEM_UID, userId); + final List<VersionedPackage> dependents = usingSharedLibraryPair.first; if (dependents == null) { continue; } diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java index fa9409f88a13..1bb0730cccb2 100644 --- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -188,7 +188,9 @@ class PackageMonitorCallbackHelper { || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE) || TextUtils.equals(action, Intent.ACTION_PACKAGE_DATA_CLEARED) - || TextUtils.equals(action, Intent.ACTION_PACKAGE_RESTARTED); + || TextUtils.equals(action, Intent.ACTION_PACKAGE_RESTARTED) + || TextUtils.equals(action, Intent.ACTION_PACKAGE_UNSTOPPED); + } private void doNotifyCallbacksByIntent(Intent intent, int userId, diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 174df44c4263..7d0a1f6afe1d 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -123,11 +123,15 @@ public class PackageSetting extends SettingBase implements PackageStateInternal @Nullable private Map<String, Set<String>> mimeGroups; + // TODO(b/314036181): encapsulate all these fields for usesSdk, instead of having three + // separate arrays. @Nullable private String[] usesSdkLibraries; @Nullable private long[] usesSdkLibrariesVersionsMajor; + @Nullable + private boolean[] usesSdkLibrariesOptional; @Nullable private String[] usesStaticLibraries; @@ -701,6 +705,9 @@ public class PackageSetting extends SettingBase implements PackageStateInternal usesSdkLibrariesVersionsMajor = other.usesSdkLibrariesVersionsMajor != null ? Arrays.copyOf(other.usesSdkLibrariesVersionsMajor, other.usesSdkLibrariesVersionsMajor.length) : null; + usesSdkLibrariesOptional = other.usesSdkLibrariesOptional != null + ? Arrays.copyOf(other.usesSdkLibrariesOptional, + other.usesSdkLibrariesOptional.length) : null; usesStaticLibraries = other.usesStaticLibraries != null ? Arrays.copyOf(other.usesStaticLibraries, @@ -1344,6 +1351,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal @NonNull @Override + public boolean[] getUsesSdkLibrariesOptional() { + return usesSdkLibrariesOptional == null ? EmptyArray.BOOLEAN : usesSdkLibrariesOptional; + } + + @NonNull + @Override public String[] getUsesStaticLibraries() { return usesStaticLibraries == null ? EmptyArray.STRING : usesStaticLibraries; } @@ -1444,6 +1457,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return this; } + public PackageSetting setUsesSdkLibrariesOptional(boolean[] usesSdkLibrariesOptional) { + this.usesSdkLibrariesOptional = usesSdkLibrariesOptional; + onChanged(); + return this; + } + public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) { this.usesStaticLibraries = usesStaticLibraries; onChanged(); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 7ee32fb2269c..52b31319cc19 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -261,7 +261,7 @@ final class RemovePackageHelper { sus = mPm.mSettings.getSharedUserSettingLPr(ps); } - mAppDataHelper.destroyAppProfilesLIF(pkg); + mAppDataHelper.destroyAppProfilesLIF(ps.getPackageName()); final List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : Collections.emptyList(); @@ -354,7 +354,7 @@ final class RemovePackageHelper { } mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); - mAppDataHelper.destroyAppProfilesLIF(resolvedPkg); + mAppDataHelper.destroyAppProfilesLIF(resolvedPkg.getPackageName()); if (outInfo != null) { outInfo.mDataRemoved = true; } diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 53b84e66840b..31a63e07b66c 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -218,7 +218,8 @@ final class ScanPackageUtils { parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user, true /*allowInstall*/, instantApp, virtualPreload, isStoppedSystemApp, UserManagerService.getInstance(), usesSdkLibraries, - parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries, + parsedPackage.getUsesSdkLibrariesVersionsMajor(), + parsedPackage.getUsesSdkLibrariesOptional(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(), newDomainSetId, parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash()); @@ -240,6 +241,7 @@ final class ScanPackageUtils { PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting), UserManagerService.getInstance(), usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(), + parsedPackage.getUsesSdkLibrariesOptional(), usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(), newDomainSetId, parsedPackage.getTargetSdkVersion(), parsedPackage.getRestrictUpdateHash()); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2cbf714792f7..75d88da059b5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -106,6 +106,7 @@ import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; +import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; @@ -339,6 +340,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags"; private static final String ATTR_SUSPENDED = "suspended"; private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; + + private static final String ATTR_OPTIONAL = "optional"; /** * @deprecated Legacy attribute, kept only for upgrading from P builds. */ @@ -942,6 +945,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile ret.setLongVersionCode(p.getVersionCode()); ret.setUsesSdkLibraries(p.getUsesSdkLibraries()); ret.setUsesSdkLibrariesVersionsMajor(p.getUsesSdkLibrariesVersionsMajor()); + ret.setUsesSdkLibrariesOptional(p.getUsesSdkLibrariesOptional()); ret.setUsesStaticLibraries(p.getUsesStaticLibraries()); ret.setUsesStaticLibrariesVersions(p.getUsesStaticLibrariesVersions()); ret.setMimeGroups(p.getMimeGroups()); @@ -1061,9 +1065,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, boolean isStoppedSystemApp, UserManagerService userManager, String[] usesSdkLibraries, long[] usesSdkLibrariesVersions, - String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, - Set<String> mimeGroupNames, @NonNull UUID domainSetId, - int targetSdkVersion, byte[] restrictUpdatedHash) { + boolean[] usesSdkLibrariesOptional, String[] usesStaticLibraries, + long[] usesStaticLibrariesVersions, Set<String> mimeGroupNames, + @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) { final PackageSetting pkgSetting; if (originalPkg != null) { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " @@ -1079,6 +1083,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setLongVersionCode(versionCode) .setUsesSdkLibraries(usesSdkLibraries) .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional) .setUsesStaticLibraries(usesStaticLibraries) .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions) // Update new package state. @@ -1096,6 +1101,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile pkgPrivateFlags, domainSetId) .setUsesSdkLibraries(usesSdkLibraries) .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional) .setUsesStaticLibraries(usesStaticLibraries) .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions) .setLegacyNativeLibraryPath(legacyNativeLibraryPath) @@ -1218,6 +1224,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions, + @Nullable boolean[] usesSdkLibrariesOptional, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, @Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) @@ -1277,12 +1284,17 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile .setRestrictUpdateHash(restrictUpdatedHash); // Update SDK library dependencies if needed. if (usesSdkLibraries != null && usesSdkLibrariesVersions != null - && usesSdkLibraries.length == usesSdkLibrariesVersions.length) { + && usesSdkLibrariesOptional != null + && usesSdkLibraries.length == usesSdkLibrariesVersions.length + && usesSdkLibraries.length == usesSdkLibrariesOptional.length) { pkgSetting.setUsesSdkLibraries(usesSdkLibraries) - .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions); + .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional); } else { pkgSetting.setUsesSdkLibraries(null) - .setUsesSdkLibrariesVersionsMajor(null); + .setUsesSdkLibrariesVersionsMajor(null) + .setUsesSdkLibrariesOptional(null); + } // Update static shared library dependencies if needed. @@ -2537,12 +2549,15 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile throws IOException, XmlPullParserException { String libName = parser.getAttributeValue(null, ATTR_NAME); long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1); + boolean optional = parser.getAttributeBoolean(null, ATTR_OPTIONAL, true); if (libName != null && libVersion >= 0) { outPs.setUsesSdkLibraries(ArrayUtils.appendElement(String.class, outPs.getUsesSdkLibraries(), libName)); outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong( outPs.getUsesSdkLibrariesVersionsMajor(), libVersion)); + outPs.setUsesSdkLibrariesOptional(PackageImpl.appendBoolean( + outPs.getUsesSdkLibrariesOptional(), optional)); } XmlUtils.skipCurrentTag(parser); @@ -2564,7 +2579,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } void writeUsesSdkLibLPw(TypedXmlSerializer serializer, String[] usesSdkLibraries, - long[] usesSdkLibraryVersions) throws IOException { + long[] usesSdkLibraryVersions, boolean[] usesSdkLibrariesOptional) throws IOException { if (ArrayUtils.isEmpty(usesSdkLibraries) || ArrayUtils.isEmpty(usesSdkLibraryVersions) || usesSdkLibraries.length != usesSdkLibraryVersions.length) { return; @@ -2573,9 +2588,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile for (int i = 0; i < libCount; i++) { final String libName = usesSdkLibraries[i]; final long libVersion = usesSdkLibraryVersions[i]; + boolean libOptional = usesSdkLibrariesOptional[i]; serializer.startTag(null, TAG_USES_SDK_LIB); serializer.attribute(null, ATTR_NAME, libName); serializer.attributeLong(null, ATTR_VERSION, libVersion); + serializer.attributeBoolean(null, ATTR_OPTIONAL, libOptional); serializer.endTag(null, TAG_USES_SDK_LIB); } } @@ -3106,7 +3123,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(), - pkg.getUsesSdkLibrariesVersionsMajor()); + pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional()); + writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(), pkg.getUsesStaticLibrariesVersions()); @@ -3206,7 +3224,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile } writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(), - pkg.getUsesSdkLibrariesVersionsMajor()); + pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional()); writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(), pkg.getUsesStaticLibrariesVersions()); @@ -5091,12 +5109,14 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile List<String> usesSdkLibraries = pkg.getUsesSdkLibraries(); long[] usesSdkLibrariesVersionsMajor = pkg.getUsesSdkLibrariesVersionsMajor(); + boolean[] usesSdkLibrariesOptional = pkg.getUsesSdkLibrariesOptional(); if (usesSdkLibraries.size() > 0) { pw.print(prefix); pw.println(" usesSdkLibraries:"); for (int i = 0, size = usesSdkLibraries.size(); i < size; ++i) { pw.print(prefix); pw.print(" "); pw.print(usesSdkLibraries.get(i)); pw.print(" version:"); - pw.println(usesSdkLibrariesVersionsMajor[i]); + pw.println(usesSdkLibrariesVersionsMajor[i]); pw.print(" optional:"); + pw.println(usesSdkLibrariesOptional[i]); } } diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java index 9384c13e583b..94495bf462f2 100644 --- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java +++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import static android.content.pm.Flags.sdkLibIndependence; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST; @@ -28,6 +27,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.content.pm.Flags; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; @@ -82,6 +82,8 @@ import java.util.function.BiConsumer; public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable { private static final boolean DEBUG_SHARED_LIBRARIES = false; + private static final String LIBRARY_TYPE_SDK = "sdk"; + /** * Apps targeting Android S and above need to declare dependencies to the public native * shared libraries that are defined by the device maker using {@code uses-native-library} tag @@ -798,8 +800,9 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable // Remove the shared library overlays from its dependent packages. for (int currentUserId : mPm.mUserManager.getUserIds()) { - final List<VersionedPackage> dependents = snapshot.getPackagesUsingSharedLibrary( - libraryInfo, 0, Process.SYSTEM_UID, currentUserId); + var usingSharedLibraryPair = snapshot.getPackagesUsingSharedLibrary(libraryInfo, 0, + Process.SYSTEM_UID, currentUserId); + final List<VersionedPackage> dependents = usingSharedLibraryPair.first; if (dependents == null) { continue; } @@ -921,42 +924,43 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable // duplicates. ArrayList<SharedLibraryInfo> usesLibraryInfos = null; if (!pkg.getUsesLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null, pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null, availablePackages, newLibraries); } if (!pkg.getUsesStaticLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(), pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(), - pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(), - usesLibraryInfos, availablePackages, newLibraries); + null, pkg.getPackageName(), "static shared", true, + pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); } if (!pkg.getUsesOptionalLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null, - pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(), + null, pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); } if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES, pkg.getPackageName(), pkg.getTargetSdkVersion())) { if (!pkg.getUsesNativeLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null, - null, pkg.getPackageName(), "native shared", true, + null, null, pkg.getPackageName(), "native shared", true, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); } if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(), - null, null, pkg.getPackageName(), "native shared", false, + null, null, null, pkg.getPackageName(), "native shared", false, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); } } if (!pkg.getUsesSdkLibraries().isEmpty()) { // Allow installation even if sdk-library dependency doesn't exist - boolean required = !sdkLibIndependence(); + boolean required = !Flags.sdkLibIndependence(); usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(), pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(), - pkg.getPackageName(), "sdk", required, pkg.getTargetSdkVersion(), + pkg.getUsesSdkLibrariesOptional(), + pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); } return usesLibraryInfos; @@ -965,6 +969,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos( @NonNull List<String> requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, + @Nullable boolean[] libsOptional, @NonNull String packageName, @NonNull String libraryType, boolean required, int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries, @NonNull final Map<String, AndroidPackage> availablePackages, @@ -981,7 +986,10 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable libName, libVersion, mSharedLibraries, newLibraries); } if (libraryInfo == null) { - if (required) { + // Only allow app be installed if the app specifies the sdk-library dependency is + // optional + if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null + && !libsOptional[i]))) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + packageName + " requires unavailable " + libraryType + " library " + libName + "; failing!"); diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 71f6c0d507d4..fe8c12c8e232 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -33,7 +33,6 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; -import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; @@ -504,10 +503,6 @@ public final class SuspendPackageHelper { final String requiredPermissionControllerPackage = getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER, userId); - final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class); - final boolean isSystemExemptFlagEnabled = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, - SYSTEM_EXEMPT_FROM_SUSPENSION, /* defaultValue= */ true); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; @@ -581,9 +576,7 @@ public final class SuspendPackageHelper { + pkg.getStaticSharedLibraryName()); continue; } - if (isSystemExemptFlagEnabled && appOpsManager.checkOpNoThrow( - AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName) - == AppOpsManager.MODE_ALLOWED) { + if (exemptFromSuspensionByAppOp(uid, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set"); continue; @@ -601,6 +594,13 @@ public final class SuspendPackageHelper { return canSuspend; } + private boolean exemptFromSuspensionByAppOp(int uid, String packageName) { + final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class); + return appOpsManager.checkOpNoThrow( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName) + == AppOpsManager.MODE_ALLOWED; + } + /** * Suspends packages on behalf of an admin. * diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b53a21c9aa1c..a7b52f4e7a58 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1519,7 +1519,7 @@ public class UserManagerService extends IUserManager.Stub { try { if (enableQuietMode) { - ActivityManager.getService().stopUser(userId, /* force= */ true, null); + stopUserForQuietMode(userId); LocalServices.getService(ActivityManagerInternal.class) .killForegroundAppsForUser(userId); } else { @@ -1547,6 +1547,18 @@ public class UserManagerService extends IUserManager.Stub { } } + private void stopUserForQuietMode(int userId) throws RemoteException { + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) { + // Allow delayed locking since some profile types want to be able to unlock again via + // biometrics. + ActivityManager.getService() + .stopUserWithDelayedLocking(userId, /* force= */ true, null); + return; + } + ActivityManager.getService().stopUser(userId, /* force= */ true, null); + } + private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode, @Nullable String callingPackage) { Slogf.i(LOG_TAG, diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 4ef8cb780734..7386301bdd6e 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -160,7 +160,9 @@ public final class UserTypeFactory { UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT) .setMediaSharedWithParent(true) .setCredentialShareableWithParent(true) - .setDeleteAppWithParent(true)); + .setDeleteAppWithParent(true) + .setCrossProfileContentSharingStrategy(UserProperties + .CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)); } /** @@ -309,6 +311,7 @@ public final class UserTypeFactory { .setStartWithParent(true) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(true) + .setAllowStoppingUserWithDelayedLocking(true) .setMediaSharedWithParent(false) .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) @@ -318,7 +321,9 @@ public final class UserTypeFactory { UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE) .setCrossProfileIntentFilterAccessControl( UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM) - .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)); + .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT) + .setCrossProfileContentSharingStrategy( + UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT)); } /** 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 d64201893400..b23dbee5e973 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -266,17 +266,20 @@ public class PackageInfoUtils { if ((flags & PackageManager.GET_ACTIVITIES) != 0) { final int N = pkg.getActivities().size(); if (N > 0) { + // Allow to match activities of quarantined packages. + long aflags = flags | PackageManager.MATCH_QUARANTINED_COMPONENTS; + int num = 0; final ActivityInfo[] res = new ActivityInfo[N]; for (int i = 0; i < N; i++) { final ParsedActivity a = pkg.getActivities().get(i); if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a, - flags)) { + aflags)) { if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals( a.getName())) { continue; } - res[num++] = generateActivityInfo(pkg, a, flags, state, + res[num++] = generateActivityInfo(pkg, a, aflags, state, applicationInfo, userId, pkgSetting); } } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java index 85d95eab2958..da58d47edbfe 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java @@ -267,6 +267,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @Nullable private String[][] usesSdkLibrariesCertDigests; @Nullable + private boolean[] usesSdkLibrariesOptional; + @Nullable @DataClass.ParcelWith(ForInternedString.class) private String sharedUserId; private int sharedUserLabel; @@ -718,16 +720,33 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @Override public PackageImpl addUsesSdkLibrary(String libraryName, long versionMajor, - String[] certSha256Digests) { + String[] certSha256Digests, boolean usesSdkLibrariesOptional) { this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries, TextUtils.safeIntern(libraryName)); this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong( this.usesSdkLibrariesVersionsMajor, versionMajor, true); this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class, this.usesSdkLibrariesCertDigests, certSha256Digests, true); + this.usesSdkLibrariesOptional = appendBoolean(this.usesSdkLibrariesOptional, + usesSdkLibrariesOptional); return this; } + /** + * Adds value to given array if not already present, providing set-like + * behavior. + */ + public static boolean[] appendBoolean(@Nullable boolean[] cur, boolean val) { + if (cur == null) { + return new boolean[] { val }; + } + final int N = cur.length; + boolean[] ret = new boolean[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + @Override public PackageImpl addUsesStaticLibrary(String libraryName, long version, String[] certSha256Digests) { @@ -1468,6 +1487,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @Override public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; } + @Nullable + @Override + public boolean[] getUsesSdkLibrariesOptional() { + return usesSdkLibrariesOptional; + } + @NonNull @Override public List<String> getUsesStaticLibraries() { @@ -3126,6 +3151,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]); } } + dest.writeBooleanArray(this.usesSdkLibrariesOptional); sForInternedString.parcel(this.sharedUserId, dest, flags); dest.writeInt(this.sharedUserLabel); @@ -3278,6 +3304,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } } } + this.usesSdkLibrariesOptional = in.createBooleanArray(); this.sharedUserId = sForInternedString.unparcel(in); this.sharedUserLabel = in.readInt(); diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java index 10b59c7230f6..a7ae4ebcb2eb 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageState.java +++ b/services/core/java/com/android/server/pm/pkg/PackageState.java @@ -322,6 +322,14 @@ public interface PackageState { long[] getUsesSdkLibrariesVersionsMajor(); /** + * @see R.styleable#AndroidManifestUsesSdkLibrary_optional + * @hide + */ + @Immutable.Ignore + @NonNull + boolean[] getUsesSdkLibrariesOptional(); + + /** * @see R.styleable#AndroidManifestUsesStaticLibrary * @hide */ diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java index 4b3992e63202..fe80f743ffc3 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateUtils.java @@ -93,8 +93,8 @@ public class PackageUserStateUtils { * this object exists means that the package must be installed or has data on at least one user; * <li> If it is not installed but still has data (i.e., it was previously uninstalled with * {@link PackageManager#DELETE_KEEP_DATA}), return true if the caller requested - * {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. - * Always available for {@link PackageManager#MATCH_ARCHIVED_PACKAGES}. + * {@link PackageManager#MATCH_UNINSTALLED_PACKAGES} or + * {@link PackageManager#MATCH_ARCHIVED_PACKAGES}; * </ul><p> */ public static boolean isAvailable(@NonNull PackageUserState state, long flags) { @@ -109,19 +109,11 @@ public class PackageUserStateUtils { if (state.isInstalled()) { if (!state.isHidden()) { return true; - } else { - return matchDataExists; - } + } else return matchDataExists; + } else { + // not installed + return matchDataExists && state.dataExists(); } - - // not installed - if (matchUninstalled) { - return state.dataExists(); - } - - // archived or installed as archived - // TODO(b/314808978): Create data folders during install-archived. - return matchArchived; } public static boolean reportIfDebug(boolean result, long flags) { diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index 722350a0d7fb..aa0fb2734382 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -2598,6 +2598,8 @@ public class ParsingPackageUtils { R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1); String certSha256Digest = sa.getNonResourceString(R.styleable .AndroidManifestUsesSdkLibrary_certDigest); + boolean optional = + sa.getBoolean(R.styleable.AndroidManifestUsesSdkLibrary_optional, false); // Since an APK providing a static shared lib can only provide the lib - fail if // malformed @@ -2641,7 +2643,8 @@ public class ParsingPackageUtils { System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, 1, additionalCertSha256Digests.length); - return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests)); + return input.success( + pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests, optional)); } finally { sa.recycle(); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 30bce2f41cf7..4e5dc1dd76fa 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -17,11 +17,13 @@ package com.android.server.policy; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; +import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY; import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; +import static android.content.PermissionChecker.PID_UNKNOWN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; @@ -117,6 +119,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -690,6 +693,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); + private final DeferredKeyActionExecutor mDeferredKeyActionExecutor = + new DeferredKeyActionExecutor(); private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; @@ -698,6 +703,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private KeyCombinationManager mKeyCombinationManager; private SingleKeyGestureDetector mSingleKeyGestureDetector; private GestureLauncherService mGestureLauncherService; + private ButtonOverridePermissionChecker mButtonOverridePermissionChecker; private boolean mLockNowPending = false; @@ -725,6 +731,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_RINGER_TOGGLE_CHORD = 24; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26; + private static final int MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE = 27; private class PolicyHandler extends Handler { @@ -792,7 +799,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAutofillManagerInternal.onBackKeyPressed(); break; case MSG_SYSTEM_KEY_PRESS: - sendSystemKeyToStatusBar((KeyEvent) msg.obj); + KeyEvent event = (KeyEvent) msg.obj; + sendSystemKeyToStatusBar(event); + event.recycle(); break; case MSG_HANDLE_ALL_APPS: launchAllAppsAction(); @@ -809,6 +818,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LOG_KEYBOARD_SYSTEM_EVENT: handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj); break; + case MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE: + final int keyCode = msg.arg1; + final long downTime = (Long) msg.obj; + mDeferredKeyActionExecutor.setActionsExecutable(keyCode, downTime); + break; } } } @@ -2234,6 +2248,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { IActivityManager getActivityManagerService() { return ActivityManager.getService(); } + + ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { + return new ButtonOverridePermissionChecker(); + } } /** {@inheritDoc} */ @@ -2499,6 +2517,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate = injector.getKeyguardServiceDelegate(); initKeyCombinationRules(); initSingleKeyGestureRules(injector.getLooper()); + mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker(); mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager); } @@ -2768,17 +2787,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mShouldEarlyShortPressOnStemPrimary) { return; } - stemPrimaryPress(1 /*count*/); + // Short-press should be triggered only if app doesn't handle it. + mDeferredKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(1 /*count*/)); } @Override void onLongPress(long eventTime) { - stemPrimaryLongPress(eventTime); + // Long-press should be triggered only if app doesn't handle it. + mDeferredKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, + eventTime, + () -> stemPrimaryLongPress(eventTime)); } @Override void onMultiPress(long downTime, int count, int unusedDisplayId) { - stemPrimaryPress(count); + // Triple-press stem to toggle accessibility gesture should always be triggered + // regardless of if app handles it. + if (count == 3 + && mTriplePressOnStemPrimaryBehavior + == TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) { + stemPrimaryPress(count); + } else { + // Other multi-press gestures should be triggered only if app doesn't handle it. + mDeferredKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(count)); + } } @Override @@ -2792,7 +2827,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp = mActivityTaskManagerInternal.getMostRecentTaskFromBackground(); if (mShouldEarlyShortPressOnStemPrimary) { - stemPrimaryPress(1 /*pressCount*/); + // Key-up gesture should be triggered only if app doesn't handle it. + mDeferredKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1)); } } } @@ -3750,6 +3787,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } break; + case KeyEvent.KEYCODE_STEM_PRIMARY: + if (prepareToSendSystemKeyToApplication(focusedToken, event)) { + // Send to app. + return false; + } else { + // Intercepted. + sendSystemKeyToStatusBarAsync(event); + return true; + } } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { @@ -3760,6 +3806,60 @@ public class PhoneWindowManager implements WindowManagerPolicy { return (metaState & KeyEvent.META_META_ON) != 0; } + /** + * In this function, we check whether a system key should be sent to the application. We also + * detect the key gesture on this key, even if the key will be sent to the app. The gesture + * action, if any, will not be executed immediately. It will be queued and execute only after + * the application tells us that it didn't handle this key. + * + * @return true if this key should be sent to the application. This also means that the target + * application has the necessary permissions to receive this key. Return false otherwise. + */ + private boolean prepareToSendSystemKeyToApplication(IBinder focusedToken, KeyEvent event) { + final int keyCode = event.getKeyCode(); + if (!event.isSystem()) { + Log.wtf( + TAG, + "Illegal keycode provided to prepareToSendSystemKeyToApplication: " + + KeyEvent.keyCodeToString(keyCode)); + return false; + } + final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; + if (isDown && event.getRepeatCount() == 0) { + // This happens at the initial DOWN event. Check focused window permission now. + final KeyInterceptionInfo info = + mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken); + if (info != null + && mButtonOverridePermissionChecker.canAppOverrideSystemKey( + mContext, info.windowOwnerUid)) { + // Focused window has the permission. Pass the event to it. + return true; + } else { + // Focused window doesn't have the permission. Intercept the event. + // If the initial DOWN event is intercepted, follow-up events will be intercepted + // too. So we know the gesture won't be handled by app, and can handle the gesture + // in system. + setDeferredKeyActionsExecutableAsync(keyCode, event.getDownTime()); + return false; + } + } else { + // This happens after the initial DOWN event. We will just reuse the initial decision. + // I.e., if the initial DOWN event was dispatched, follow-up events should be + // dispatched. Otherwise, follow-up events should be consumed. + final Set<Integer> consumedKeys = mConsumedKeysForDevice.get(event.getDeviceId()); + final boolean wasConsumed = consumedKeys != null && consumedKeys.contains(keyCode); + return !wasConsumed; + } + } + + private void setDeferredKeyActionsExecutableAsync(int keyCode, long downTime) { + Message msg = Message.obtain(mHandler, MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE); + msg.arg1 = keyCode; + msg.obj = downTime; + msg.setAsynchronous(true); + msg.sendToTarget(); + } + @SuppressLint("MissingPermission") private void injectBackGesture(long downtime) { // Create and inject down event @@ -3977,11 +4077,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.closeSystemDialogs(); } return true; + case KeyEvent.KEYCODE_STEM_PRIMARY: + handleUnhandledSystemKey(event); + sendSystemKeyToStatusBarAsync(event); + return true; } return false; } + /** + * Called when a system key was sent to application and was unhandled. We will execute any + * queued actions associated with this key code at this point. + */ + private void handleUnhandledSystemKey(KeyEvent event) { + if (!event.isSystem()) { + Log.wtf( + TAG, + "Illegal keycode provided to handleUnhandledSystemKey: " + + KeyEvent.keyCodeToString(event.getKeyCode())); + return; + } + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + // If the initial DOWN event is unhandled by app, follow-up events will also be + // unhandled by app. So we can handle the key event in system. + setDeferredKeyActionsExecutableAsync(event.getKeyCode(), event.getDownTime()); + } + } + private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), direction).sendToTarget(); @@ -4904,9 +5027,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MACRO_4: result &= ~ACTION_PASS_TO_USER; break; - case KeyEvent.KEYCODE_STEM_PRIMARY: - sendSystemKeyToStatusBarAsync(event); - break; } if (useHapticFeedback) { @@ -5016,7 +5136,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * Notify the StatusBar that a system key was pressed without blocking the current thread. */ private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) { - Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyEvent); + // Make a copy because the event may be recycled. + Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, KeyEvent.obtain(keyEvent)); message.setAsynchronous(true); mHandler.sendMessage(message); } @@ -6468,6 +6589,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mGlobalKeyManager.dump(prefix, pw); mKeyCombinationManager.dump(prefix, pw); mSingleKeyGestureDetector.dump(prefix, pw); + mDeferredKeyActionExecutor.dump(prefix, pw); if (mWakeGestureListener != null) { mWakeGestureListener.dump(pw, prefix); @@ -6793,4 +6915,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { + " name."); } } + + /** A helper class to check button override permission. */ + static class ButtonOverridePermissionChecker { + boolean canAppOverrideSystemKey(Context context, int uid) { + return PermissionChecker.checkPermissionForDataDelivery( + context, + OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW, + PID_UNKNOWN, + uid, + null, + null, + null) + == PERMISSION_GRANTED; + } + } } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 99653ae1cd72..24d7acd772c1 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -759,6 +759,36 @@ public class ThermalManagerService extends SystemService { case "NPU": type = Temperature.TYPE_NPU; break; + case "TPU": + type = Temperature.TYPE_TPU; + break; + case "DISPLAY": + type = Temperature.TYPE_DISPLAY; + break; + case "MODEM": + type = Temperature.TYPE_MODEM; + break; + case "SOC": + type = Temperature.TYPE_SOC; + break; + case "WIFI": + type = Temperature.TYPE_WIFI; + break; + case "CAMERA": + type = Temperature.TYPE_CAMERA; + break; + case "FLASHLIGHT": + type = Temperature.TYPE_FLASHLIGHT; + break; + case "SPEAKER": + type = Temperature.TYPE_SPEAKER; + break; + case "AMBIENT": + type = Temperature.TYPE_AMBIENT; + break; + case "POGO": + type = Temperature.TYPE_POGO; + break; default: pw.println("Invalid temperature type: " + typeName); return -1; diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java index 43fd15d690e6..6fbbc0f072e8 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java @@ -19,7 +19,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.os.BatteryConsumer; -import com.android.internal.os.MultiStateStats; import com.android.internal.os.PowerStats; import java.lang.annotation.Retention; diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java index 5fd8ddfbf240..7feb9643fb8f 100644 --- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java @@ -19,7 +19,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; -import com.android.internal.os.MultiStateStats; import com.android.internal.os.PowerStats; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index 4442845f83b2..1af127175f80 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -78,7 +78,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private final SparseArray<UidStats> mUidStats = new SparseArray<>(); private boolean mIsPerUidTimeInStateSupported; private PowerStatsInternal mPowerStatsInternal; - private int[] mCpuEnergyConsumerIds; + private int[] mCpuEnergyConsumerIds = new int[0]; private PowerStats.Descriptor mPowerStatsDescriptor; // Reusable instance private PowerStats mCpuPowerStats; @@ -286,8 +286,6 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { if (mPowerStatsInternal != null) { readCpuEnergyConsumerIds(); - } else { - mCpuEnergyConsumerIds = new int[0]; } int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount(); @@ -320,7 +318,6 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private void readCpuEnergyConsumerIds() { EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo(); if (energyConsumerInfo == null) { - mCpuEnergyConsumerIds = new int[0]; return; } diff --git a/core/java/com/android/internal/os/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java index ecfed537b798..935695008a9a 100644 --- a/core/java/com/android/internal/os/MultiStateStats.java +++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package com.android.internal.os; +package com.android.server.power.stats; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.LongArrayMultiStateCounter; import com.android.internal.util.Preconditions; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java index 0facb9c01d74..1637022f705d 100644 --- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java +++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.util.IndentingPrintWriter; import android.util.SparseArray; -import com.android.internal.os.MultiStateStats; import com.android.internal.os.PowerStats; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java index 1f6f11320f1b..c267b799e2a4 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java @@ -22,7 +22,6 @@ import android.os.BatteryUsageStats; import android.os.UidBatteryConsumer; import android.util.Slog; -import com.android.internal.os.MultiStateStats; import com.android.internal.os.PowerStats; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index d172d3fd4139..eac4fc00b667 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -865,21 +865,19 @@ public class TrustManagerService extends SystemService { mDeviceLockedForUser.put(userId, locked); } if (changed) { - dispatchDeviceLocked(userId, locked); - Authorization.onLockScreenEvent(locked, userId, null, - getBiometricSids(userId)); + notifyTrustAgentsOfDeviceLockState(userId, locked); + notifyKeystoreOfDeviceLockState(userId, locked); // Also update the user's profiles who have unified challenge, since they // share the same unlocked state (see {@link #isDeviceLocked(int)}) for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) { if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) { - Authorization.onLockScreenEvent(locked, profileHandle, null, - getBiometricSids(profileHandle)); + notifyKeystoreOfDeviceLockState(profileHandle, locked); } } } } - private void dispatchDeviceLocked(int userId, boolean isLocked) { + private void notifyTrustAgentsOfDeviceLockState(int userId, boolean isLocked) { for (int i = 0; i < mActiveAgents.size(); i++) { AgentInfo agent = mActiveAgents.valueAt(i); if (agent.userId == userId) { @@ -892,6 +890,17 @@ public class TrustManagerService extends SystemService { } } + private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) { + if (isLocked) { + Authorization.onDeviceLocked(userId, getBiometricSids(userId)); + } else { + // Notify Keystore that the device is now unlocked for the user. Note that for unlocks + // with LSKF, this is redundant with the call from LockSettingsService which provides + // the password. However, for unlocks with biometric or trust agent, this is required. + Authorization.onDeviceUnlocked(userId, /* password= */ null); + } + } + private void dispatchEscrowTokenActivatedLocked(long handle, int userId) { for (int i = 0; i < mActiveAgents.size(); i++) { AgentInfo agent = mActiveAgents.valueAt(i); @@ -1425,10 +1434,10 @@ public class TrustManagerService extends SystemService { } } - private long[] getBiometricSids(int userId) { + private @NonNull long[] getBiometricSids(int userId) { BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class); if (biometricManager == null) { - return null; + return new long[0]; } return biometricManager.getAuthenticatorIds(userId); } @@ -1680,8 +1689,7 @@ public class TrustManagerService extends SystemService { mDeviceLockedForUser.put(userId, locked); } - Authorization.onLockScreenEvent(locked, userId, null, - getBiometricSids(userId)); + notifyKeystoreOfDeviceLockState(userId, locked); if (locked) { try { diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index e4c7fc1f3797..4c4dafa0aa70 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1851,14 +1851,14 @@ public final class TvInputManagerService extends SystemService { sessionState.currentChannel = channelUri; notifyCurrentChannelInfosUpdatedLocked(userState); if (!sessionState.isRecordingSession) { - String actualInputId = getActualInputId(sessionState); - if (!TextUtils.equals(mOnScreenInputId, actualInputId)) { + String sessionActualInputId = getSessionActualInputId(sessionState); + if (!TextUtils.equals(mOnScreenInputId, sessionActualInputId)) { logExternalInputEvent( FrameworkStatsLog .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED, - actualInputId, sessionState); + sessionState.inputId, sessionState); } - mOnScreenInputId = actualInputId; + mOnScreenInputId = sessionActualInputId; mOnScreenSessionState = sessionState; } } @@ -2985,11 +2985,20 @@ public final class TvInputManagerService extends SystemService { // e.g. if an HDMI port has a CEC device plugged in, the actual input id of the HDMI // session should be the input id of CEC device instead of the default HDMI input id. @GuardedBy("mLock") - private String getActualInputId(SessionState sessionState) { + private String getSessionActualInputId(SessionState sessionState) { UserState userState = getOrCreateUserStateLocked(sessionState.userId); TvInputState tvInputState = userState.inputMap.get(sessionState.inputId); + if (tvInputState == null) { + Slog.w(TAG, "No TvInputState for sessionState.inputId " + sessionState.inputId); + return sessionState.inputId; + } TvInputInfo tvInputInfo = tvInputState.info; - String actualInputId = sessionState.inputId; + if (tvInputInfo == null) { + Slog.w(TAG, "TvInputInfo is null for input id " + sessionState.inputId); + return sessionState.inputId; + } + + String sessionActualInputId = sessionState.inputId; switch (tvInputInfo.getType()) { case TvInputInfo.TYPE_HDMI: // TODO: find a better approach towards active CEC device in future @@ -2997,13 +3006,13 @@ public final class TvInputManagerService extends SystemService { mTvInputHardwareManager.getHdmiParentInputMap(); if (hdmiParentInputMap.containsKey(sessionState.inputId)) { List<String> parentInputList = hdmiParentInputMap.get(sessionState.inputId); - actualInputId = parentInputList.get(0); + sessionActualInputId = parentInputList.get(0); } break; default: break; } - return actualInputId; + return sessionActualInputId; } @Nullable @@ -3110,8 +3119,22 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private void logExternalInputEvent(int eventType, String inputId, SessionState sessionState) { UserState userState = getOrCreateUserStateLocked(sessionState.userId); - TvInputState tvInputState = userState.inputMap.get(inputId); + // Try finding the actual input id in inputMap. If not found, find the input id as is. + String inputIdForLogging = getSessionActualInputId(sessionState); + TvInputState tvInputState = userState.inputMap.get(inputIdForLogging); + if (tvInputState == null) { + inputIdForLogging = inputId; + tvInputState = userState.inputMap.get(inputIdForLogging); + } + if (tvInputState == null) { + Slog.w(TAG, "Cannot find input state for input id " + inputIdForLogging); + return; + } TvInputInfo tvInputInfo = tvInputState.info; + if (tvInputInfo == null) { + Slog.w(TAG, "TvInputInfo is null for input id " + inputIdForLogging); + return; + } int inputState = tvInputState.state; int inputType = tvInputInfo.getType(); String displayName = tvInputInfo.loadLabel(mContext).toString(); @@ -3647,14 +3670,14 @@ public final class TvInputManagerService extends SystemService { mSessionState.currentChannel = channelUri; notifyCurrentChannelInfosUpdatedLocked(userState); if (!mSessionState.isRecordingSession) { - String actualInputId = getActualInputId(mSessionState); - if (!TextUtils.equals(mOnScreenInputId, actualInputId)) { + String sessionActualInputId = getSessionActualInputId(mSessionState); + if (!TextUtils.equals(mOnScreenInputId, sessionActualInputId)) { logExternalInputEvent( FrameworkStatsLog .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED, - actualInputId, mSessionState); + mSessionState.inputId, mSessionState); } - mOnScreenInputId = actualInputId; + mOnScreenInputId = sessionActualInputId; mOnScreenSessionState = mSessionState; } } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 7862f58374a3..e501b9dc9959 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -146,17 +146,31 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements mGrantedUriPermissions = new SparseArray<>(); private UriGrantsManagerService() { - this(SystemServiceManager.ensureSystemDir()); + this(SystemServiceManager.ensureSystemDir(), "uri-grants"); } - private UriGrantsManagerService(File systemDir) { + private UriGrantsManagerService(File systemDir, String commitTag) { mH = new H(IoThread.get().getLooper()); - mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants"); + final File file = new File(systemDir, "urigrants.xml"); + mGrantFile = (commitTag != null) ? new AtomicFile(file, commitTag) : new AtomicFile(file); } @VisibleForTesting static UriGrantsManagerService createForTest(File systemDir) { - final UriGrantsManagerService service = new UriGrantsManagerService(systemDir); + final UriGrantsManagerService service = new UriGrantsManagerService(systemDir, null) { + @VisibleForTesting + protected int checkUidPermission(String permission, int uid) { + // Tests have no permission granted + return PackageManager.PERMISSION_DENIED; + } + + @VisibleForTesting + protected int checkComponentPermission(String permission, int uid, int owningUid, + boolean exported) { + // Tests have no permission granted + return PackageManager.PERMISSION_DENIED; + } + }; service.mAmInternal = LocalServices.getService(ActivityManagerInternal.class); service.mPmInternal = LocalServices.getService(PackageManagerInternal.class); return service; @@ -202,7 +216,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements } } - private int checkUidPermission(String permission, int uid) { + @VisibleForTesting + protected int checkUidPermission(String permission, int uid) { try { return AppGlobals.getPackageManager().checkUidPermission(permission, uid); } catch (RemoteException e) { @@ -210,6 +225,12 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements } } + @VisibleForTesting + protected int checkComponentPermission(String permission, int uid, int owningUid, + boolean exported) { + return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); + } + /** * Grant uri permissions to the specified app. * @@ -916,7 +937,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) { if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid); if (UserHandle.getUserId(uid) != grantUri.sourceUserId) { - if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true) + if (checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true) != PERMISSION_GRANTED) { return false; } @@ -1340,7 +1361,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { return true; } - return ActivityManager.checkComponentPermission( + return checkComponentPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid, /* owningUid = */-1, /* exported = */ true) == PackageManager.PERMISSION_GRANTED; diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java index 2b6dffb2b271..7b5192c4bd6b 100644 --- a/services/core/java/com/android/server/utils/AnrTimer.java +++ b/services/core/java/com/android/server/utils/AnrTimer.java @@ -16,37 +16,22 @@ package com.android.server.utils; -import static android.text.TextUtils.formatSimple; - -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Handler; -import android.os.Looper; import android.os.Message; -import android.os.Process; import android.os.SystemClock; import android.os.Trace; -import android.text.TextUtils; import android.text.format.TimeMigrationUtils; -import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Log; -import android.util.MathUtils; -import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.Keep; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.ProcessCpuTracker; import com.android.internal.util.RingBuffer; import java.io.PrintWriter; -import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; /** * This class managers AnrTimers. An AnrTimer is a substitute for a delayed Message. In legacy @@ -102,12 +87,6 @@ public class AnrTimer<V> { private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER; /** - * Enable tracing from the time a timer expires until it is accepted or discarded. This is - * used to diagnose long latencies in the client. - */ - private static final boolean ENABLE_TRACING = false; - - /** * Return true if the feature is enabled. By default, the value is take from the Flags class * but it can be changed for local testing. */ @@ -116,22 +95,13 @@ public class AnrTimer<V> { } /** - * The status of an ANR timer. TIMER_INVALID status is returned when an error is detected. + * This class allows test code to provide instance-specific overrides. */ - private static final int TIMER_INVALID = 0; - private static final int TIMER_RUNNING = 1; - private static final int TIMER_EXPIRED = 2; - - @IntDef(prefix = { "TIMER_" }, value = { - TIMER_INVALID, TIMER_RUNNING, TIMER_EXPIRED - }) - private @interface TimerStatus {} - - /** - * A static list of all known AnrTimer instances, used for dumping and testing. - */ - @GuardedBy("sAnrTimerList") - private static final ArrayList<WeakReference<AnrTimer>> sAnrTimerList = new ArrayList<>(); + static class Injector { + boolean anrTimerServiceEnabled() { + return AnrTimer.anrTimerServiceEnabled(); + } + } /** * An error is defined by its issue, the operation that detected the error, the tag of the @@ -161,6 +131,22 @@ public class AnrTimer<V> { this.arg = arg; this.timestamp = SystemClock.elapsedRealtime(); } + + /** + * Dump a single error to the output stream. + */ + private void dump(IndentingPrintWriter ipw, int seq) { + ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, operation, tag, issue, arg); + + final long offset = System.currentTimeMillis() - SystemClock.elapsedRealtime(); + final long etime = offset + timestamp; + ipw.println(" date:" + TimeMigrationUtils.formatMillisWithFixedFormat(etime)); + ipw.increaseIndent(); + for (int i = 0; i < stack.length; i++) { + ipw.println(" " + stack[i].toString()); + } + ipw.decreaseIndent(); + } } /** @@ -171,132 +157,10 @@ public class AnrTimer<V> { @GuardedBy("sErrors") private static final RingBuffer<Error> sErrors = new RingBuffer<>(Error.class, 20); - /** - * A record of a single anr timer. The pid and uid are retained for reference but they do not - * participate in the equality tests. A {@link Timer} is bound to its parent {@link AnrTimer} - * through the owner field. Access to timer fields is guarded by the mLock of the owner. - */ - private static class Timer { - /** The AnrTimer that is managing this Timer. */ - final AnrTimer owner; - - /** The argument that uniquely identifies the Timer in the context of its current owner. */ - final Object arg; - /** The pid of the process being tracked by this Timer. */ - final int pid; - /** The uid of the process being tracked by this Timer as reported by the kernel. */ - final int uid; - /** The original timeout. */ - final long timeoutMs; - - /** The status of the Timer. */ - @GuardedBy("owner.mLock") - @TimerStatus - int status; - - /** The absolute time the timer was startd */ - final long startedMs; - - /** Fields used by the native timer service. */ - - /** The timer ID: used to exchange information with the native service. */ - int timerId; - - /** Fields used by the legacy timer service. */ - - /** - * The process's cpu delay time when the timer starts . It is meaningful only if - * extendable is true. The cpu delay is cumulative, so the incremental delay that occurs - * during a timer is the delay at the end of the timer minus this value. Units are in - * milliseconds. - */ - @GuardedBy("owner.mLock") - long initialCpuDelayMs; - - /** True if the timer has been extended. */ - @GuardedBy("owner.mLock") - boolean extended; - - /** - * Fetch a new Timer. This is private. Clients should get a new timer using the obtain() - * method. - */ - private Timer(int pid, int uid, @Nullable Object arg, long timeoutMs, - @NonNull AnrTimer service) { - this.arg = arg; - this.pid = pid; - this.uid = uid; - this.timerId = 0; - this.timeoutMs = timeoutMs; - this.startedMs = now(); - this.owner = service; - this.initialCpuDelayMs = 0; - this.extended = false; - this.status = TIMER_INVALID; - } - - /** Get a timer. This implementation constructs a new timer. */ - static Timer obtain(int pid, int uid, @Nullable Object arg, long timeout, - @NonNull AnrTimer service) { - return new Timer(pid, uid, arg, timeout, service); - } - - /** Release a timer. This implementation simply drops the timer. */ - void release() { - } - - /** Return the age of the timer. This is used for debugging. */ - long age() { - return now() - startedMs; - } - - /** - * The hash code is generated from the owner and the argument. By definition, the - * combination must be unique for the lifetime of an in-use Timer. - */ - @Override - public int hashCode() { - return Objects.hash(owner, arg); - } - - /** - * The equality check compares the owner and the argument. By definition, the combination - * must be unique for the lifetime of an in-use Timer. - */ - @Override - public boolean equals(Object r) { - if (r instanceof Timer) { - Timer t = (Timer) r; - return Objects.equals(owner, t.owner) && Objects.equals(arg, t.arg); - } - return false; - } - - @Override - public String toString() { - final int myStatus; - synchronized (owner.mLock) { - myStatus = status; - } - return "timerId=" + timerId + " pid=" + pid + " uid=" + uid - + " " + statusString(myStatus) + " " + owner.mLabel; - } - } - /** A lock for the AnrTimer instance. */ private final Object mLock = new Object(); /** - * The map from client argument to the associated timer. - */ - @GuardedBy("mLock") - private final ArrayMap<V, Timer> mTimerMap = new ArrayMap<>(); - - /** The highwater mark of started, but not closed, timers. */ - @GuardedBy("mLock") - private int mMaxStarted = 0; - - /** * The total number of timers started. */ @GuardedBy("mLock") @@ -309,176 +173,6 @@ public class AnrTimer<V> { private int mTotalErrors = 0; /** - * The total number of timers that have expired. - */ - @GuardedBy("mLock") - private int mTotalExpired = 0; - - /** - * A TimerService that generates a timeout event <n> milliseconds in the future. See the - * class documentation for an explanation of the operations. - */ - private abstract class TimerService { - /** Start a timer. The timeout must be initialized. */ - abstract boolean start(@NonNull Timer timer); - - abstract void cancel(@NonNull Timer timer); - - abstract void accept(@NonNull Timer timer); - - abstract void discard(@NonNull Timer timer); - } - - /** - * A class to assist testing. All methods are null by default but can be overridden as - * necessary for a test. - */ - @VisibleForTesting - static class Injector { - private final Handler mReferenceHandler; - - Injector(@NonNull Handler handler) { - mReferenceHandler = handler; - } - - /** - * Return a handler for the given Callback, based on the reference handler. The handler - * might be mocked, in which case it does not have a valid Looper. In this case, use the - * main Looper. - */ - @NonNull - Handler newHandler(@NonNull Handler.Callback callback) { - Looper looper = mReferenceHandler.getLooper(); - if (looper == null) looper = Looper.getMainLooper(); - return new Handler(looper, callback); - } - - /** - * Return a CpuTracker. The default behavior is to create a new CpuTracker but this changes - * for unit tests. - **/ - @NonNull - CpuTracker newTracker() { - return new CpuTracker(); - } - - /** Return true if the feature is enabled. */ - boolean isFeatureEnabled() { - return anrTimerServiceEnabled(); - } - } - - /** - * A helper class to measure CPU delays. Given a process ID, this class will return the - * cumulative CPU delay for the PID, since process inception. This class is defined to assist - * testing. - */ - @VisibleForTesting - static class CpuTracker { - /** - * The parameter to ProcessCpuTracker indicates that statistics should be collected on a - * single process and not on the collection of threads associated with that process. - */ - private final ProcessCpuTracker mCpu = new ProcessCpuTracker(false); - - /** A simple wrapper to fetch the delay. This method can be overridden for testing. */ - long delay(int pid) { - return mCpu.getCpuDelayTimeForPid(pid); - } - } - - /** - * The "user-space" implementation of the timer service. This service uses its own message - * handler to create timeouts. - */ - private class HandlerTimerService extends TimerService { - /** The lock for this handler */ - private final Object mLock = new Object(); - - /** The message handler for scheduling future events. */ - private final Handler mHandler; - - /** The interface to fetch process statistics that might extend an ANR timeout. */ - private final CpuTracker mCpu; - - /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */ - @VisibleForTesting - HandlerTimerService(@NonNull Injector injector) { - mHandler = injector.newHandler(this::expires); - mCpu = injector.newTracker(); - } - - /** Post a message with the specified timeout. The timer is not modified. */ - private void post(@NonNull Timer t, long timeoutMillis) { - final Message msg = mHandler.obtainMessage(); - msg.obj = t; - mHandler.sendMessageDelayed(msg, timeoutMillis); - } - - /** - * The local expiration handler first attempts to compute a timer extension. If the timer - * should be extended, it is rescheduled in the future (granting more time to the - * associated process). If the timer should not be extended then the timeout is delivered - * to the client. - * - * A process is extended to account for the time the process was swapped out and was not - * runnable through no fault of its own. A timer can only be extended once and only if - * the AnrTimer permits extensions. Finally, a timer will never be extended by more than - * the original timeout, so the total timeout will never be more than twice the originally - * configured timeout. - */ - private boolean expires(Message msg) { - Timer t = (Timer) msg.obj; - synchronized (mLock) { - long extension = 0; - if (mExtend && !t.extended) { - extension = mCpu.delay(t.pid) - t.initialCpuDelayMs; - if (extension < 0) extension = 0; - if (extension > t.timeoutMs) extension = t.timeoutMs; - t.extended = true; - } - if (extension > 0) { - post(t, extension); - } else { - onExpiredLocked(t); - } - } - return true; - } - - @GuardedBy("mLock") - @Override - boolean start(@NonNull Timer t) { - if (mExtend) { - t.initialCpuDelayMs = mCpu.delay(t.pid); - } - post(t, t.timeoutMs); - return true; - } - - @Override - void cancel(@NonNull Timer t) { - mHandler.removeMessages(0, t); - } - - @Override - void accept(@NonNull Timer t) { - // Nothing to do. - } - - @Override - void discard(@NonNull Timer t) { - // Nothing to do. - } - - /** The string identifies this subclass of AnrTimerService as being based on handlers. */ - @Override - public String toString() { - return "handler"; - } - } - - /** * The handler for messages sent from this instance. */ private final Handler mHandler; @@ -499,18 +193,6 @@ public class AnrTimer<V> { private final boolean mExtend; /** - * The timer service to use for this AnrTimer. - */ - private final TimerService mTimerService; - - /** - * Whether or not canceling a non-existent timer is an error. Clients often cancel freely - * preemptively, without knowing if the timer was ever started. Keeping this variable true - * means that such behavior is not an error. - */ - private final boolean mLenientCancel = true; - - /** * The top-level switch for the feature enabled or disabled. */ private final FeatureSwitch mFeature; @@ -528,40 +210,34 @@ public class AnrTimer<V> { * AnrTimer may extend the individual timer rather than immediately delivering the timeout to * the client. The extension policy is not part of the instance. * - * This method accepts an {@link #Injector} to tune behavior for testing. This method should - * not be called directly by regular clients. - * * @param handler The handler to which the expiration message will be delivered. * @param what The "what" parameter for the expiration message. * @param label A name for this instance. * @param extend A flag to indicate if expired timers can be granted extensions. - * @param injector An {@link #Injector} to tune behavior for testing. + * @param injector An injector to provide overrides for testing. */ @VisibleForTesting AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend, - @NonNull Injector injector) { + @NonNull Injector injector) { mHandler = handler; mWhat = what; mLabel = label; mExtend = extend; - boolean enabled = injector.isFeatureEnabled(); - if (!enabled) { - mFeature = new FeatureDisabled(); - mTimerService = null; - } else { - mFeature = new FeatureEnabled(); - mTimerService = new HandlerTimerService(injector); - - synchronized (sAnrTimerList) { - sAnrTimerList.add(new WeakReference(this)); - } - } - Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label)); + mFeature = new FeatureDisabled(); } /** - * Create an AnrTimer instance with the default {@link #Injector}. See {@link AnrTimer(Handler, - * int, String, boolean, Injector} for a functional description. + * Create one AnrTimer instance. The instance is given a handler and a "what". Individual + * timers are started with {@link #start}. If a timer expires, then a {@link Message} is sent + * immediately to the handler with {@link Message.what} set to what and {@link Message.obj} set + * to the timer key. + * + * AnrTimer instances have a label, which must be unique. The label is used for reporting and + * debug. + * + * If an individual timer expires internally, and the "extend" parameter is true, then the + * AnrTimer may extend the individual timer rather than immediately delivering the timeout to + * the client. The extension policy is not part of the instance. * * @param handler The handler to which the expiration message will be delivered. * @param what The "what" parameter for the expiration message. @@ -569,7 +245,7 @@ public class AnrTimer<V> { * @param extend A flag to indicate if expired timers can be granted extensions. */ public AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) { - this(handler, what, label, extend, new Injector(handler)); + this(handler, what, label, extend, new Injector()); } /** @@ -596,105 +272,17 @@ public class AnrTimer<V> { } /** - * Start a trace on the timer. The trace is laid down in the AnrTimerTrack. - */ - private void traceBegin(Timer t, String what) { - if (ENABLE_TRACING) { - final String label = formatSimple("%s(%d,%d,%s)", what, t.pid, t.uid, mLabel); - final int cookie = t.hashCode(); - Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK, label, cookie); - } - } - - /** - * End a trace on the timer. - */ - private void traceEnd(Timer t) { - if (ENABLE_TRACING) { - final int cookie = t.hashCode(); - Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK, cookie); - } - } - - /** - * Return the string representation for a timer status. - */ - private static String statusString(int s) { - switch (s) { - case TIMER_INVALID: return "invalid"; - case TIMER_RUNNING: return "running"; - case TIMER_EXPIRED: return "expired"; - } - return formatSimple("unknown: %d", s); - } - - /** - * Delete the timer associated with arg from the maps and return it. Return null if the timer - * was not found. - */ - @GuardedBy("mLock") - private Timer removeLocked(V arg) { - Timer timer = mTimerMap.remove(arg); - return timer; - } - - /** - * Return the number of timers currently running. - */ - @VisibleForTesting - static int sizeOfTimerList() { - synchronized (sAnrTimerList) { - int totalTimers = 0; - for (int i = 0; i < sAnrTimerList.size(); i++) { - AnrTimer client = sAnrTimerList.get(i).get(); - if (client != null) totalTimers += client.mTimerMap.size(); - } - return totalTimers; - } - } - - /** - * Clear out all existing timers. This will lead to unexpected behavior if used carelessly. - * It is available only for testing. It returns the number of times that were actually - * erased. - */ - @VisibleForTesting - static int resetTimerListForHermeticTest() { - synchronized (sAnrTimerList) { - int mapLen = 0; - for (int i = 0; i < sAnrTimerList.size(); i++) { - AnrTimer client = sAnrTimerList.get(i).get(); - if (client != null) { - mapLen += client.mTimerMap.size(); - client.mTimerMap.clear(); - } - } - if (mapLen > 0) { - Log.w(TAG, formatSimple("erasing timer list: clearing %d timers", mapLen)); - } - return mapLen; - } - } - - /** - * Generate a log message for a timer. - */ - private void report(@NonNull Timer timer, @NonNull String msg) { - Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg)); - } - - /** * The FeatureSwitch class provides a quick switch between feature-enabled behavior and * feature-disabled behavior. */ private abstract class FeatureSwitch { - abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs); + abstract void start(@NonNull V arg, int pid, int uid, long timeoutMs); - abstract boolean cancel(@NonNull V arg); + abstract void cancel(@NonNull V arg); - abstract boolean accept(@NonNull V arg); + abstract void accept(@NonNull V arg); - abstract boolean discard(@NonNull V arg); + abstract void discard(@NonNull V arg); abstract boolean enabled(); } @@ -706,29 +294,25 @@ public class AnrTimer<V> { private class FeatureDisabled extends FeatureSwitch { /** Start a timer by sending a message to the client's handler. */ @Override - boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + void start(@NonNull V arg, int pid, int uid, long timeoutMs) { final Message msg = mHandler.obtainMessage(mWhat, arg); mHandler.sendMessageDelayed(msg, timeoutMs); - return true; } /** Cancel a timer by removing the message from the client's handler. */ @Override - boolean cancel(@NonNull V arg) { + void cancel(@NonNull V arg) { mHandler.removeMessages(mWhat, arg); - return true; } /** accept() is a no-op when the feature is disabled. */ @Override - boolean accept(@NonNull V arg) { - return true; + void accept(@NonNull V arg) { } /** discard() is a no-op when the feature is disabled. */ @Override - boolean discard(@NonNull V arg) { - return true; + void discard(@NonNull V arg) { } /** The feature is not enabled. */ @@ -739,113 +323,6 @@ public class AnrTimer<V> { } /** - * The FeatureEnabled class enables the AnrTimer logic. It is used when the AnrTimer service - * is enabled via Flags.anrTimerServiceEnabled. - */ - private class FeatureEnabled extends FeatureSwitch { - - /** - * Start a timer. - */ - @Override - boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { - final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this); - synchronized (mLock) { - Timer old = mTimerMap.get(arg); - // There is an existing timer. If the timer was running, then cancel the running - // timer and restart it. If the timer was expired record a protocol error and - // discard the expired timer. - if (old != null) { - if (old.status == TIMER_EXPIRED) { - restartedLocked(old.status, arg); - discard(arg); - } else { - cancel(arg); - } - } - if (mTimerService.start(timer)) { - timer.status = TIMER_RUNNING; - mTimerMap.put(arg, timer); - mTotalStarted++; - mMaxStarted = Math.max(mMaxStarted, mTimerMap.size()); - if (DEBUG) report(timer, "start"); - return true; - } else { - Log.e(TAG, "AnrTimer.start failed"); - return false; - } - } - } - - /** - * Cancel a timer. Return false if the timer was not found. - */ - @Override - boolean cancel(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - if (!mLenientCancel) notFoundLocked("cancel", arg); - return false; - } - mTimerService.cancel(timer); - // There may be an expiration message in flight. Cancel it. - mHandler.removeMessages(mWhat, arg); - if (DEBUG) report(timer, "cancel"); - timer.release(); - return true; - } - } - - /** - * Accept a timer in the framework-level handler. The timeout has been accepted and the - * timeout handler is executing. Return false if the timer was not found. - */ - @Override - boolean accept(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("accept", arg); - return false; - } - mTimerService.accept(timer); - traceEnd(timer); - if (DEBUG) report(timer, "accept"); - timer.release(); - return true; - } - } - - /** - * Discard a timer in the framework-level handler. For whatever reason, the timer is no - * longer interesting. No statistics are collected. Return false if the time was not - * found. - */ - @Override - boolean discard(@NonNull V arg) { - synchronized (mLock) { - Timer timer = removeLocked(arg); - if (timer == null) { - notFoundLocked("discard", arg); - return false; - } - mTimerService.discard(timer); - traceEnd(timer); - if (DEBUG) report(timer, "discard"); - timer.release(); - return true; - } - } - - /** The feature is enabled. */ - @Override - boolean enabled() { - return true; - } - } - - /** * Start a timer associated with arg. The same object must be used to cancel, accept, or * discard a timer later. If a timer already exists with the same arg, then the existing timer * is canceled and a new timer is created. @@ -854,32 +331,27 @@ public class AnrTimer<V> { * @param pid The Linux process ID of the target being timed. * @param uid The Linux user ID of the target being timed. * @param timeoutMs The timer timeout, in milliseconds. - * @return true if the timer was successfully created. */ - public boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { - return mFeature.start(arg, pid, uid, timeoutMs); + public void start(@NonNull V arg, int pid, int uid, long timeoutMs) { + mFeature.start(arg, pid, uid, timeoutMs); } /** * Cancel the running timer associated with arg. The timer is forgotten. If the timer has * expired, the call is treated as a discard. No errors are reported if the timer does not * exist or if the timer has expired. - * - * @return true if the timer was found and was running. */ - public boolean cancel(@NonNull V arg) { - return mFeature.cancel(arg); + public void cancel(@NonNull V arg) { + mFeature.cancel(arg); } /** * Accept the expired timer associated with arg. This indicates that the caller considers the * timer expiration to be a true ANR. (See {@link #discard} for an alternate response.) It is * an error to accept a running timer, however the running timer will be canceled. - * - * @return true if the timer was found and was expired. */ - public boolean accept(@NonNull V arg) { - return mFeature.accept(arg); + public void accept(@NonNull V arg) { + mFeature.accept(arg); } /** @@ -889,24 +361,9 @@ public class AnrTimer<V> { * such a process could be stopped at a breakpoint and its failure to respond would not be an * error. It is an error to discard a running timer, however the running timer will be * canceled. - * - * @return true if the timer was found and was expired. */ - public boolean discard(@NonNull V arg) { - return mFeature.discard(arg); - } - - /** - * The notifier that a timer has fired. The timer is not modified. - */ - @GuardedBy("mLock") - private void onExpiredLocked(@NonNull Timer timer) { - if (DEBUG) report(timer, "expire"); - traceBegin(timer, "expired"); - mHandler.sendMessage(Message.obtain(mHandler, mWhat, timer.arg)); - synchronized (mLock) { - mTotalExpired++; - } + public void discard(@NonNull V arg) { + mFeature.discard(arg); } /** @@ -916,9 +373,7 @@ public class AnrTimer<V> { synchronized (mLock) { pw.format("timer: %s\n", mLabel); pw.increaseIndent(); - pw.format("started=%d maxStarted=%d running=%d expired=%d error=%d\n", - mTotalStarted, mMaxStarted, mTimerMap.size(), - mTotalExpired, mTotalErrors); + pw.format("started=%d errors=%d\n", mTotalStarted, mTotalErrors); pw.decreaseIndent(); } } @@ -931,10 +386,20 @@ public class AnrTimer<V> { } /** - * The current time in milliseconds. + * Dump all errors to the output stream. */ - private static long now() { - return SystemClock.uptimeMillis(); + private static void dumpErrors(IndentingPrintWriter ipw) { + Error errors[]; + synchronized (sErrors) { + if (sErrors.size() == 0) return; + errors = sErrors.toArray(); + } + ipw.println("Errors"); + ipw.increaseIndent(); + for (int i = 0; i < errors.length; i++) { + if (errors[i] != null) errors[i].dump(ipw, i); + } + ipw.decreaseIndent(); } /** @@ -966,60 +431,12 @@ public class AnrTimer<V> { } /** - * Log an error about a timer that is started when there is an existing timer. - */ - @GuardedBy("mLock") - private void restartedLocked(@TimerStatus int status, Object arg) { - recordErrorLocked("start", status == TIMER_EXPIRED ? "autoDiscard" : "autoCancel", arg); - } - - /** - * Dump a single error to the output stream. - */ - private static void dump(IndentingPrintWriter ipw, int seq, Error err) { - ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag, - err.issue, err.arg); - - final long offset = System.currentTimeMillis() - SystemClock.elapsedRealtime(); - final long etime = offset + err.timestamp; - ipw.println(" date:" + TimeMigrationUtils.formatMillisWithFixedFormat(etime)); - ipw.increaseIndent(); - for (int i = 0; i < err.stack.length; i++) { - ipw.println(" " + err.stack[i].toString()); - } - ipw.decreaseIndent(); - } - - /** - * Dump all errors to the output stream. - */ - private static void dumpErrors(IndentingPrintWriter ipw) { - Error errors[]; - synchronized (sErrors) { - if (sErrors.size() == 0) return; - errors = sErrors.toArray(); - } - ipw.println("Errors"); - ipw.increaseIndent(); - for (int i = 0; i < errors.length; i++) { - if (errors[i] != null) dump(ipw, i, errors[i]); - } - ipw.decreaseIndent(); - } - - /** * Dumpsys output. */ public static void dump(@NonNull PrintWriter pw, boolean verbose) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.println("AnrTimer statistics"); ipw.increaseIndent(); - synchronized (sAnrTimerList) { - for (int i = 0; i < sAnrTimerList.size(); i++) { - AnrTimer client = sAnrTimerList.get(i).get(); - if (client != null) client.dump(ipw); - } - } if (verbose) dumpErrors(ipw); ipw.format("AnrTimerEnd\n"); ipw.decreaseIndent(); diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS index be91611deccc..fbc0b56c2eb7 100644 --- a/services/core/java/com/android/server/utils/OWNERS +++ b/services/core/java/com/android/server/utils/OWNERS @@ -10,3 +10,8 @@ per-file Watcher.java = file:/services/core/java/com/android/server/pm/OWNERS per-file Watcher.java = shombert@google.com per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS per-file EventLogger.java = jmtrivi@google.com + +# Bug component : 158088 = per-file AnrTimer*.java +per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS + +per-file flags.aconfig = file:/PERFORMANCE_OWNERS diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java index 0386e66aaf3d..b8850afdf3ad 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java @@ -142,6 +142,20 @@ public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable return (T) mStorage.valueAt(intIndex, valueIndex); } + /** + * Copy from another SparseSetArray. + */ + public void copyFrom(@NonNull SparseSetArray<T> c) { + clear(); + final int end = c.size(); + for (int i = 0; i < end; i++) { + final int key = c.keyAt(i); + final ArraySet<T> set = c.get(key); + mStorage.addAll(key, set); + } + onChanged(); + } + @NonNull @Override public Object snapshot() { diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java deleted file mode 100644 index 2eeb903bb551..000000000000 --- a/services/core/java/com/android/server/vibrator/VibratorControlService.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.vibrator; - -import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.frameworks.vibrator.IVibratorControlService; -import android.frameworks.vibrator.IVibratorController; -import android.frameworks.vibrator.VibrationParam; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Slog; - -import java.util.Objects; - -/** - * Implementation of {@link IVibratorControlService} which allows the registration of - * {@link IVibratorController} to set and receive vibration params. - * - * @hide - */ -public final class VibratorControlService extends IVibratorControlService.Stub { - private static final String TAG = "VibratorControlService"; - - private final VibratorControllerHolder mVibratorControllerHolder; - private final Object mLock; - - public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) { - mVibratorControllerHolder = vibratorControllerHolder; - mLock = lock; - } - - @Override - public void registerVibratorController(IVibratorController controller) - throws RemoteException { - synchronized (mLock) { - mVibratorControllerHolder.setVibratorController(controller); - } - } - - @Override - public void unregisterVibratorController(@NonNull IVibratorController controller) - throws RemoteException { - Objects.requireNonNull(controller); - - synchronized (mLock) { - if (mVibratorControllerHolder.getVibratorController() == null) { - Slog.w(TAG, "Received request to unregister IVibratorController = " - + controller + ", but no controller was previously registered. Request " - + "Ignored."); - return; - } - if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), - controller.asBinder())) { - Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided " - + "controller doesn't match the registered one. " + this); - return; - } - mVibratorControllerHolder.setVibratorController(null); - } - } - - @Override - public void setVibrationParams( - @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token) - throws RemoteException { - // TODO(b/305939964): Add set vibration implementation. - } - - @Override - public void clearVibrationParams(int types, IVibratorController token) throws RemoteException { - // TODO(b/305939964): Add clear vibration implementation. - } - - @Override - public void onRequestVibrationParamsComplete( - IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) - throws RemoteException { - // TODO(305942827): Cache the vibration params in VibrationScaler - } - - @Override - public int getInterfaceVersion() throws RemoteException { - return this.VERSION; - } - - @Override - public String getInterfaceHash() throws RemoteException { - return this.HASH; - } -} diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java deleted file mode 100644 index 63e69db9480f..000000000000 --- a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.vibrator; - -import android.annotation.NonNull; -import android.frameworks.vibrator.IVibratorController; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Slog; - -/** - * Holder class for {@link IVibratorController}. - * - * @hide - */ -public final class VibratorControllerHolder implements IBinder.DeathRecipient { - private static final String TAG = "VibratorControllerHolder"; - - private IVibratorController mVibratorController; - - public IVibratorController getVibratorController() { - return mVibratorController; - } - - /** - * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new - * controller. This will also take care of registering and unregistering death notifications - * for the cached {@link IVibratorController}. - */ - public void setVibratorController(IVibratorController controller) { - try { - if (mVibratorController != null) { - mVibratorController.asBinder().unlinkToDeath(this, 0); - } - mVibratorController = controller; - if (mVibratorController != null) { - mVibratorController.asBinder().linkToDeath(this, 0); - } - } catch (RemoteException e) { - Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e); - } - } - - @Override - public void binderDied(@NonNull IBinder deadBinder) { - if (deadBinder == mVibratorController.asBinder()) { - setVibratorController(null); - } - } - - @Override - public void binderDied() { - // Should not be used as binderDied(IBinder who) is overridden. - Slog.wtf(TAG, "binderDied() called unexpectedly."); - } -} diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index d5044d9bc660..7d4bd3baf613 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -53,7 +53,6 @@ import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.VibratorInfo; -import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibratorInfoFactory; @@ -88,13 +87,10 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; - /** System implementation of {@link IVibratorManagerService}. */ public class VibratorManagerService extends IVibratorManagerService.Stub { private static final String TAG = "VibratorManagerService"; private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; - private static final String VIBRATOR_CONTROL_SERVICE = - "android.frameworks.vibrator.IVibratorControlService/default"; private static final boolean DEBUG = false; private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); @@ -273,10 +269,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); - if (Flags.adaptiveHapticsEnabled()) { - injector.addService(VIBRATOR_CONTROL_SERVICE, - new VibratorControlService(new VibratorControllerHolder(), mLock)); - } } /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ @@ -411,9 +403,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Override // Binder call public void performHapticFeedback( - int uid, int deviceId, String opPkg, int constant, boolean always, String reason, - IBinder token) { - performHapticFeedbackInternal(uid, deviceId, opPkg, constant, always, reason, token); + int uid, int deviceId, String opPkg, int constant, boolean always, String reason) { + // Note that the `performHapticFeedback` method does not take a token argument from the + // caller, and instead, uses this service as the token. This is to mitigate performance + // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are + // short-lived, so we don't need to cancel when the process dies. + performHapticFeedbackInternal( + uid, deviceId, opPkg, constant, always, reason, /* token= */ this); } /** diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index b1abe2a567e8..1577cef9de00 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -107,6 +107,7 @@ import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; +import com.android.window.flags.Flags; import java.io.File; import java.io.IOException; @@ -758,6 +759,11 @@ final class AccessibilityController { if (!isMagnifierActivated) { break; } + if (Flags.doNotCheckIntersectionWhenNonMagnifiableWindowTransitions()) { + if (!windowState.shouldMagnify()) { + break; + } + } switch (type) { case WindowManager.LayoutParams.TYPE_APPLICATION: case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index e5794a1f7efd..dfb2a5fcf9fa 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1121,6 +1121,7 @@ class ActivityStarter { callerApp, request.originatingPendingIntent, request.forcedBalByPiSender, + resultRecord, intent, checkedOptions); request.logMessage.append(" (").append(balVerdict).append(")"); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 91f45a7f0ae3..73edb4bed6ca 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -103,6 +103,7 @@ import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORD import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; @@ -118,6 +119,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.wm.BackgroundActivityStartController.BalVerdict; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -2242,7 +2244,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final BackgroundActivityStartController balController = mTaskSupervisor.getBackgroundActivityLaunchController(); - if (balController.shouldAbortBackgroundActivityStart( + final BalVerdict balVerdict = balController.checkBackgroundActivityStart( callingUid, callingPid, callingPackage, @@ -2252,10 +2254,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { null, BackgroundStartPrivileges.NONE, null, - null)) { - if (!isBackgroundActivityStartsEnabled()) { - return; - } + null, + null); + if (balVerdict.blocks() && !isBackgroundActivityStartsEnabled()) { + Slog.w(TAG, "moveTaskToFront blocked: " + balVerdict); + return; + } + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "moveTaskToFront allowed: " + balVerdict); } try { final Task task = mRootWindowContainer.anyTaskForId(taskId); @@ -4259,7 +4265,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void dumpActivityContainersLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER CONTAINERS (dumpsys activity containers)"); - mRootWindowContainer.dumpChildrenNames(pw, " "); + mRootWindowContainer.dumpChildrenNames(pw, ""); pw.println(" "); } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 761b0a8f0b39..50de0b08f3b0 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; +import static com.android.server.wm.BackgroundActivityStartController.BalVerdict; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; @@ -31,6 +33,7 @@ import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.util.Slog; /** * An implementation of IAppTask, that allows an app to manage its own tasks via @@ -123,7 +126,7 @@ class AppTaskImpl extends IAppTask.Stub { } final BackgroundActivityStartController balController = mService.mTaskSupervisor.getBackgroundActivityLaunchController(); - if (balController.shouldAbortBackgroundActivityStart( + BalVerdict balVerdict = balController.checkBackgroundActivityStart( callingUid, callingPid, callingPackage, @@ -133,10 +136,14 @@ class AppTaskImpl extends IAppTask.Stub { null, BackgroundStartPrivileges.NONE, null, - null)) { - if (!mService.isBackgroundActivityStartsEnabled()) { - return; - } + null, + null); + if (balVerdict.blocks() && !mService.isBackgroundActivityStartsEnabled()) { + Slog.w(TAG, "moveTaskToFront blocked: : " + balVerdict); + return; + } + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "moveTaskToFront allowed: " + balVerdict); } } mService.mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId, diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index c5902c965b43..c3f1e41d4c5e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -258,11 +258,11 @@ class BackNavigationController { // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; - } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) { - // skip if current activity is translucent + } else if (hasTranslucentActivity(currentActivity, prevActivities)) { + // skip if one of participant activity is translucent backType = BackNavigationInfo.TYPE_CALLBACK; } else if (prevActivities.size() > 0) { - if (!isOccluded || prevActivities.get(0).canShowWhenLocked()) { + if (!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities)) { // We have another Activity in the same currentTask to go to final WindowContainer parent = currentActivity.getParent(); final boolean canCustomize = parent != null @@ -307,7 +307,7 @@ class BackNavigationController { findAdjacentActivityIfExist(tmpPre, prevActivities); } if (prevTask == null || prevActivities.isEmpty() - || (isOccluded && !prevActivities.get(0).canShowWhenLocked())) { + || (isOccluded && !isAllActivitiesCanShowWhenLocked(prevActivities))) { backType = BackNavigationInfo.TYPE_CALLBACK; } else if (prevTask.isActivityTypeHome()) { removedWindowContainer = currentTask; @@ -508,6 +508,30 @@ class BackNavigationController { outList.add(topActivity); } + private static boolean hasTranslucentActivity(@NonNull ActivityRecord currentActivity, + @NonNull ArrayList<ActivityRecord> prevActivities) { + if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) { + return true; + } + for (int i = prevActivities.size() - 1; i >= 0; --i) { + final ActivityRecord test = prevActivities.get(i); + if (!test.occludesParent() || test.showWallpaper()) { + return true; + } + } + return false; + } + + private static boolean isAllActivitiesCanShowWhenLocked( + @NonNull ArrayList<ActivityRecord> prevActivities) { + for (int i = prevActivities.size() - 1; i >= 0; --i) { + if (!prevActivities.get(i).canShowWhenLocked()) { + return false; + } + } + return !prevActivities.isEmpty(); + } + boolean isMonitoringTransition() { return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote(); } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index f8b22c97e218..92665af1075b 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -205,27 +205,6 @@ public class BackgroundActivityStartController { return activity != null && packageName.equals(activity.getPackageName()); } - /** - * @see #checkBackgroundActivityStart(int, int, String, int, int, WindowProcessController, - * PendingIntentRecord, BackgroundStartPrivileges, Intent, ActivityOptions) - */ - boolean shouldAbortBackgroundActivityStart( - int callingUid, - int callingPid, - final String callingPackage, - int realCallingUid, - int realCallingPid, - WindowProcessController callerApp, - PendingIntentRecord originatingPendingIntent, - BackgroundStartPrivileges forcedBalByPiSender, - Intent intent, - ActivityOptions checkedOptions) { - return checkBackgroundActivityStart(callingUid, callingPid, callingPackage, - realCallingUid, realCallingPid, - callerApp, originatingPendingIntent, - forcedBalByPiSender, intent, checkedOptions).blocks(); - } - private class BalState { private final String mCallingPackage; @@ -255,6 +234,7 @@ public class BackgroundActivityStartController { WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, BackgroundStartPrivileges forcedBalByPiSender, + ActivityRecord resultRecord, Intent intent, ActivityOptions checkedOptions) { this.mCallingPackage = callingPackage; @@ -267,7 +247,9 @@ public class BackgroundActivityStartController { mOriginatingPendingIntent = originatingPendingIntent; mIntent = intent; mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid); - if (originatingPendingIntent == null) { + if (originatingPendingIntent == null // not a PendingIntent + || resultRecord != null // sent for result + ) { // grant BAL privileges unless explicitly opted out mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator = checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode() @@ -443,6 +425,8 @@ public class BackgroundActivityStartController { // indicates BAL would be blocked because only creator of the PI has the privilege to allow // BAL, the sender does not have the privilege to allow BAL. private boolean mOnlyCreatorAllows; + /** indicates that this verdict is based on the real calling UID and not the calling UID */ + private boolean mBasedOnRealCaller; BalVerdict(@BalCode int balCode, boolean background, String message) { this.mBackground = background; @@ -472,6 +456,15 @@ public class BackgroundActivityStartController { return mOnlyCreatorAllows; } + private BalVerdict setBasedOnRealCaller() { + mBasedOnRealCaller = true; + return this; + } + + private boolean isBasedOnRealCaller() { + return mBasedOnRealCaller; + } + public String toString() { StringBuilder builder = new StringBuilder(); builder.append(balCodeToString(mCode)); @@ -495,7 +488,15 @@ public class BackgroundActivityStartController { return builder.toString(); } + public @BalCode int getRawCode() { + return mCode; + } + public @BalCode int getCode() { + if (mBasedOnRealCaller && mCode != BAL_BLOCK) { + // for compatibility always return BAL_ALLOW_PENDING_INTENT if based on real caller + return BAL_ALLOW_PENDING_INTENT; + } return mCode; } } @@ -516,6 +517,7 @@ public class BackgroundActivityStartController { * @param forcedBalByPiSender If set to allow, the * PendingIntent's sender will try to force allow background activity starts. * This is only possible if the sender of the PendingIntent is a system process. + * @param resultRecord If not null, this indicates that the caller expects a result. * @param intent Intent that should be started. * @param checkedOptions ActivityOptions to allow specific opt-ins/opt outs. * @@ -531,6 +533,7 @@ public class BackgroundActivityStartController { WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, BackgroundStartPrivileges forcedBalByPiSender, + ActivityRecord resultRecord, Intent intent, ActivityOptions checkedOptions) { @@ -541,7 +544,7 @@ public class BackgroundActivityStartController { BalState state = new BalState(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, - forcedBalByPiSender, intent, checkedOptions); + forcedBalByPiSender, resultRecord, intent, checkedOptions); // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a // visible window. @@ -580,7 +583,8 @@ public class BackgroundActivityStartController { // PendingIntents is null). BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows() ? resultForCaller - : checkBackgroundActivityStartAllowedBySender(state, checkedOptions); + : checkBackgroundActivityStartAllowedBySender(state, checkedOptions) + .setBasedOnRealCaller(); if (state.isPendingIntent()) { resultForCaller.setOnlyCreatorAllows( resultForCaller.allows() && resultForRealCaller.blocks()); @@ -828,7 +832,7 @@ public class BackgroundActivityStartController { && ActivityManager.checkComponentPermission( android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, state.mRealCallingUid, NO_PROCESS_UID, true) == PackageManager.PERMISSION_GRANTED) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ false, "realCallingUid has BAL permission."); } @@ -839,18 +843,18 @@ public class BackgroundActivityStartController { || state.mAppSwitchState == APP_SWITCH_FG_ONLY; if (Flags.balImproveRealCallerVisibilityCheck()) { if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "realCallingUid has visible window"); } if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "realCallingUid has non-app visible window"); } } else { // don't abort if the realCallingUid has a visible window // TODO(b/171459802): We should check appSwitchAllowed also if (state.mRealCallingUidHasAnyVisibleWindow) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "realCallingUid has visible (non-toast) window."); } @@ -860,7 +864,7 @@ public class BackgroundActivityStartController { // wasn't allowed to start an activity if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts() && state.mIsRealCallingUidPersistentSystemProcess) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false, "realCallingUid is persistent system process AND intent " + "sender forced to allow."); @@ -868,7 +872,7 @@ public class BackgroundActivityStartController { // don't abort if the realCallingUid is an associated companion app if (mService.isAssociatedCompanionApp( UserHandle.getUserId(state.mRealCallingUid), state.mRealCallingUid)) { - return new BalVerdict(BAL_ALLOW_PENDING_INTENT, + return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT, /*background*/ false, "realCallingUid is a companion app."); } @@ -1469,7 +1473,7 @@ public class BackgroundActivityStartController { intent != null ? intent.getComponent().flattenToShortString() : ""; FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED, activityName, - code, + BAL_ALLOW_PENDING_INTENT, callingUid, realCallingUid); } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 9a32dc8d9190..478524b7bd1c 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -22,7 +22,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS; import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; -import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY; +import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION; @@ -49,6 +49,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; +import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.ArrayList; @@ -113,13 +114,17 @@ class BackgroundLaunchProcessController { "process allowed by token"); } // Allow if the caller is bound by a UID that's currently foreground. - if (isBoundByForegroundUid()) { + // But still respect the appSwitchState. + boolean allowBoundByForegroundUid = + Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid() + ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid() + : isBoundByForegroundUid(); + if (allowBoundByForegroundUid) { return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "process bound by foreground uid"); } // Allow if the caller has an activity in any foreground task. - if (hasActivityInVisibleTask - && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) { + if (hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW) { return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false, "process has activity in foreground task"); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 794711262a75..be7c18c49373 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -23,7 +23,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; import static android.app.WindowConfiguration.windowingModeToString; import static android.app.WindowConfigurationProto.WINDOWING_MODE; @@ -739,17 +741,43 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * level with the input prefix. */ public void dumpChildrenNames(PrintWriter pw, String prefix) { - final String childPrefix = prefix + " "; + dumpChildrenNames(pw, prefix, true /* isLastChild */); + } + + /** + * Dumps the names of this container children in the input print writer indenting each + * level with the input prefix. + */ + public void dumpChildrenNames(PrintWriter pw, String prefix, boolean isLastChild) { + int curWinMode = getWindowingMode(); + String winMode = windowingModeToString(curWinMode); + if (curWinMode != WINDOWING_MODE_UNDEFINED && + curWinMode != WINDOWING_MODE_FULLSCREEN) { + winMode = winMode.toUpperCase(); + } + int requestedWinMode = getRequestedOverrideWindowingMode(); + String overrideWinMode = windowingModeToString(requestedWinMode); + if (requestedWinMode != WINDOWING_MODE_UNDEFINED && + requestedWinMode != WINDOWING_MODE_FULLSCREEN) { + overrideWinMode = overrideWinMode.toUpperCase(); + } + String actType = activityTypeToString(getActivityType()); + if (getActivityType() != ACTIVITY_TYPE_UNDEFINED + && getActivityType() != ACTIVITY_TYPE_STANDARD) { + actType = actType.toUpperCase(); + } + pw.print(prefix + (isLastChild ? "└─ " : "├─ ")); pw.println(getName() - + " type=" + activityTypeToString(getActivityType()) - + " mode=" + windowingModeToString(getWindowingMode()) - + " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()) + + " type=" + actType + + " mode=" + winMode + + " override-mode=" + overrideWinMode + " requested-bounds=" + getRequestedOverrideBounds().toShortString() + " bounds=" + getBounds().toShortString()); + + String childPrefix = prefix + (isLastChild ? " " : "│ "); for (int i = getChildCount() - 1; i >= 0; --i) { final E cc = getChildAt(i); - pw.print(childPrefix + "#" + i + " "); - cc.dumpChildrenNames(pw, childPrefix); + cc.dumpChildrenNames(pw, childPrefix, i == 0 /* isLastChild */); } } diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java new file mode 100644 index 000000000000..975fdc0ade5d --- /dev/null +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.TRANSIT_CHANGE; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS; +import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; +import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; +import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.view.DisplayInfo; +import android.window.DisplayAreaInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.display.BrightnessSynchronizer; +import com.android.internal.protolog.common.ProtoLog; +import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A DisplayUpdater that could defer and queue display updates coming from DisplayManager to + * WindowManager. It allows to defer pending display updates if WindowManager is currently not + * ready to apply them. + * For example, this might happen if there is a Shell transition running and physical display + * changed. We can't immediately apply the display updates because we want to start a separate + * display change transition. In this case, we will queue all display updates until the current + * transition's collection finishes and then apply them afterwards. + */ +public class DeferredDisplayUpdater implements DisplayUpdater { + + /** + * List of fields that could be deferred before applying to DisplayContent. + * This should be kept in sync with {@link DeferredDisplayUpdater#calculateDisplayInfoDiff} + */ + @VisibleForTesting + static final DisplayInfoFieldsUpdater DEFERRABLE_FIELDS = (out, override) -> { + // Treat unique id and address change as WM-specific display change as we re-query display + // settings and parameters based on it which could cause window changes + out.uniqueId = override.uniqueId; + out.address = override.address; + + // Also apply WM-override fields, since they might produce differences in window hierarchy + WM_OVERRIDE_FIELDS.setFields(out, override); + }; + + private final DisplayContent mDisplayContent; + + @NonNull + private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); + + /** + * The last known display parameters from DisplayManager, some WM-specific fields in this object + * might not be applied to the DisplayContent yet + */ + @Nullable + private DisplayInfo mLastDisplayInfo; + + /** + * The last DisplayInfo that was applied to DisplayContent, only WM-specific parameters must be + * used from this object. This object is used to store old values of DisplayInfo while these + * fields are pending to be applied to DisplayContent. + */ + @Nullable + private DisplayInfo mLastWmDisplayInfo; + + @NonNull + private final DisplayInfo mOutputDisplayInfo = new DisplayInfo(); + + public DeferredDisplayUpdater(@NonNull DisplayContent displayContent) { + mDisplayContent = displayContent; + mNonOverrideDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); + } + + /** + * Reads the latest display parameters from the display manager and returns them in a callback. + * If there are pending display updates, it will wait for them to finish first and only then it + * will call the callback with the latest display parameters. + * + * @param finishCallback is called when all pending display updates are finished + */ + @Override + public void updateDisplayInfo(@NonNull Runnable finishCallback) { + // Get the latest display parameters from the DisplayManager + final DisplayInfo displayInfo = getCurrentDisplayInfo(); + + final int displayInfoDiff = calculateDisplayInfoDiff(mLastDisplayInfo, displayInfo); + final boolean physicalDisplayUpdated = isPhysicalDisplayUpdated(mLastDisplayInfo, + displayInfo); + + mLastDisplayInfo = displayInfo; + + // Apply whole display info immediately as is if either: + // * it is the first display update + // * shell transitions are disabled or temporary unavailable + if (displayInfoDiff == DIFF_EVERYTHING + || !mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { + ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS, + "DeferredDisplayUpdater: applying DisplayInfo immediately"); + + mLastWmDisplayInfo = displayInfo; + applyLatestDisplayInfo(); + finishCallback.run(); + return; + } + + // If there are non WM-specific display info changes, apply only these fields immediately + if ((displayInfoDiff & DIFF_NOT_WM_DEFERRABLE) > 0) { + ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS, + "DeferredDisplayUpdater: partially applying DisplayInfo immediately"); + applyLatestDisplayInfo(); + } + + // If there are WM-specific display info changes, apply them through a Shell transition + if ((displayInfoDiff & DIFF_WM_DEFERRABLE) > 0) { + ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS, + "DeferredDisplayUpdater: deferring DisplayInfo update"); + + requestDisplayChangeTransition(physicalDisplayUpdated, () -> { + // Apply deferrable fields to DisplayContent only when the transition + // starts collecting, non-deferrable fields are ignored in mLastWmDisplayInfo + mLastWmDisplayInfo = displayInfo; + applyLatestDisplayInfo(); + finishCallback.run(); + }); + } else { + // There are no WM-specific updates, so we can immediately notify that all display + // info changes are applied + finishCallback.run(); + } + } + + /** + * Requests a display change Shell transition + * + * @param physicalDisplayUpdated if true also starts remote display change + * @param onStartCollect called when the Shell transition starts collecting + */ + private void requestDisplayChangeTransition(boolean physicalDisplayUpdated, + @NonNull Runnable onStartCollect) { + + final Transition transition = new Transition(TRANSIT_CHANGE, /* flags= */ 0, + mDisplayContent.mTransitionController, + mDisplayContent.mTransitionController.mSyncEngine); + + mDisplayContent.mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); + + mDisplayContent.mTransitionController.startCollectOrQueue(transition, deferred -> { + final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth, + mDisplayContent.mInitialDisplayHeight); + final int fromRotation = mDisplayContent.getRotation(); + + onStartCollect.run(); + + ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS, + "DeferredDisplayUpdater: applied DisplayInfo after deferring"); + + if (physicalDisplayUpdated) { + onDisplayUpdated(transition, fromRotation, startBounds); + } else { + transition.setAllReady(); + } + }); + } + + /** + * Applies current DisplayInfo to DisplayContent, DisplayContent is merged from two parts: + * - non-deferrable fields are set from the most recent values received from DisplayManager + * (uses {@link mLastDisplayInfo} field) + * - deferrable fields are set from the latest values that we could apply to WM + * (uses {@link mLastWmDisplayInfo} field) + */ + private void applyLatestDisplayInfo() { + copyDisplayInfoFields(mOutputDisplayInfo, /* base= */ mLastDisplayInfo, + /* override= */ mLastWmDisplayInfo, /* fields= */ DEFERRABLE_FIELDS); + mDisplayContent.onDisplayInfoUpdated(mOutputDisplayInfo); + } + + @NonNull + private DisplayInfo getCurrentDisplayInfo() { + mDisplayContent.mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo( + mDisplayContent.mDisplayId, mNonOverrideDisplayInfo); + return new DisplayInfo(mNonOverrideDisplayInfo); + } + + /** + * Called when physical display is updated, this could happen e.g. on foldable + * devices when the physical underlying display is replaced. This method should be called + * when the new display info is already applied to the WM hierarchy. + * + * @param fromRotation rotation before the display change + * @param startBounds display bounds before the display change + */ + private void onDisplayUpdated(@NonNull Transition transition, int fromRotation, + @NonNull Rect startBounds) { + final Rect endBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth, + mDisplayContent.mInitialDisplayHeight); + final int toRotation = mDisplayContent.getRotation(); + + final TransitionRequestInfo.DisplayChange displayChange = + new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId()); + displayChange.setStartAbsBounds(startBounds); + displayChange.setEndAbsBounds(endBounds); + displayChange.setStartRotation(fromRotation); + displayChange.setEndRotation(toRotation); + displayChange.setPhysicalDisplayChanged(true); + + mDisplayContent.mTransitionController.requestStartTransition(transition, + /* startTask= */ null, /* remoteTransition= */ null, displayChange); + + final DisplayAreaInfo newDisplayAreaInfo = mDisplayContent.getDisplayAreaInfo(); + + final boolean startedRemoteChange = mDisplayContent.mRemoteDisplayChangeController + .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo, + transaction -> finishDisplayUpdate(transaction, transition)); + + if (!startedRemoteChange) { + finishDisplayUpdate(/* wct= */ null, transition); + } + } + + private void finishDisplayUpdate(@Nullable WindowContainerTransaction wct, + @NonNull Transition transition) { + if (wct != null) { + mDisplayContent.mAtmService.mWindowOrganizerController.applyTransaction( + wct); + } + transition.setAllReady(); + } + + private boolean isPhysicalDisplayUpdated(@Nullable DisplayInfo first, + @Nullable DisplayInfo second) { + if (first == null || second == null) return true; + return !Objects.equals(first.uniqueId, second.uniqueId); + } + + /** + * Diff result: fields are the same + */ + static final int DIFF_NONE = 0; + + /** + * Diff result: fields that could be deferred in WM are different + */ + static final int DIFF_WM_DEFERRABLE = 1 << 0; + + /** + * Diff result: fields that could not be deferred in WM are different + */ + static final int DIFF_NOT_WM_DEFERRABLE = 1 << 1; + + /** + * Diff result: everything is different + */ + static final int DIFF_EVERYTHING = 0XFFFFFFFF; + + @VisibleForTesting + static int calculateDisplayInfoDiff(@Nullable DisplayInfo first, @Nullable DisplayInfo second) { + int diff = DIFF_NONE; + + if (Objects.equals(first, second)) return diff; + if (first == null || second == null) return DIFF_EVERYTHING; + + if (first.layerStack != second.layerStack + || first.flags != second.flags + || first.type != second.type + || first.displayId != second.displayId + || first.displayGroupId != second.displayGroupId + || !Objects.equals(first.deviceProductInfo, second.deviceProductInfo) + || first.modeId != second.modeId + || first.renderFrameRate != second.renderFrameRate + || first.defaultModeId != second.defaultModeId + || first.userPreferredModeId != second.userPreferredModeId + || !Arrays.equals(first.supportedModes, second.supportedModes) + || first.colorMode != second.colorMode + || !Arrays.equals(first.supportedColorModes, second.supportedColorModes) + || !Objects.equals(first.hdrCapabilities, second.hdrCapabilities) + || !Arrays.equals(first.userDisabledHdrTypes, second.userDisabledHdrTypes) + || first.minimalPostProcessingSupported != second.minimalPostProcessingSupported + || first.appVsyncOffsetNanos != second.appVsyncOffsetNanos + || first.presentationDeadlineNanos != second.presentationDeadlineNanos + || first.state != second.state + || first.committedState != second.committedState + || first.ownerUid != second.ownerUid + || !Objects.equals(first.ownerPackageName, second.ownerPackageName) + || first.removeMode != second.removeMode + || first.getRefreshRate() != second.getRefreshRate() + || first.brightnessMinimum != second.brightnessMinimum + || first.brightnessMaximum != second.brightnessMaximum + || first.brightnessDefault != second.brightnessDefault + || first.installOrientation != second.installOrientation + || !Objects.equals(first.layoutLimitedRefreshRate, second.layoutLimitedRefreshRate) + || !BrightnessSynchronizer.floatEquals(first.hdrSdrRatio, second.hdrSdrRatio) + || !first.thermalRefreshRateThrottling.contentEquals( + second.thermalRefreshRateThrottling) + || !Objects.equals(first.thermalBrightnessThrottlingDataId, + second.thermalBrightnessThrottlingDataId)) { + diff |= DIFF_NOT_WM_DEFERRABLE; + } + + if (first.appWidth != second.appWidth + || first.appHeight != second.appHeight + || first.smallestNominalAppWidth != second.smallestNominalAppWidth + || first.smallestNominalAppHeight != second.smallestNominalAppHeight + || first.largestNominalAppWidth != second.largestNominalAppWidth + || first.largestNominalAppHeight != second.largestNominalAppHeight + || first.logicalWidth != second.logicalWidth + || first.logicalHeight != second.logicalHeight + || first.physicalXDpi != second.physicalXDpi + || first.physicalYDpi != second.physicalYDpi + || first.rotation != second.rotation + || !Objects.equals(first.displayCutout, second.displayCutout) + || first.logicalDensityDpi != second.logicalDensityDpi + || !Objects.equals(first.roundedCorners, second.roundedCorners) + || !Objects.equals(first.displayShape, second.displayShape) + || !Objects.equals(first.uniqueId, second.uniqueId) + || !Objects.equals(first.address, second.address) + ) { + diff |= DIFF_WM_DEFERRABLE; + } + + return diff; + } +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 50376fed2005..1861b2ef5fa3 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -276,6 +276,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import static com.android.window.flags.Flags.deferDisplayUpdates; /** * Utility class for keeping track of the WindowStates and other pertinent contents of a @@ -545,10 +546,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean isDefaultDisplay; /** Detect user tapping outside of current focused task bounds .*/ + // TODO(b/315321016): Remove once pointer event detection is removed from WM. @VisibleForTesting final TaskTapPointerEventListener mTapDetector; /** Detect user tapping outside of current focused root task bounds .*/ + // TODO(b/315321016): Remove once pointer event detection is removed from WM. private Region mTouchExcludeRegion = new Region(); /** Save allocating when calculating rects */ @@ -1158,7 +1161,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWallpaperController.resetLargestDisplay(display); display.getDisplayInfo(mDisplayInfo); display.getMetrics(mDisplayMetrics); - mDisplayUpdater = new ImmediateDisplayUpdater(this); + if (deferDisplayUpdates()) { + mDisplayUpdater = new DeferredDisplayUpdater(this); + } else { + mDisplayUpdater = new ImmediateDisplayUpdater(this); + } mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp * mDisplayMetrics.densityDpi / DENSITY_DEFAULT; isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; @@ -1189,12 +1196,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp "PointerEventDispatcher" + mDisplayId, mDisplayId); mPointerEventDispatcher = new PointerEventDispatcher(inputChannel); - // Tap Listeners are supported for: - // 1. All physical displays (multi-display). - // 2. VirtualDisplays on VR, AA (and everything else). - mTapDetector = new TaskTapPointerEventListener(mWmService, this); - registerPointerEventListener(mTapDetector); - registerPointerEventListener(mWmService.mMousePositionTracker); + if (com.android.input.flags.Flags.removePointerEventTrackingInWm()) { + mTapDetector = null; + } else { + // Tap Listeners are supported for: + // 1. All physical displays (multi-display). + // 2. VirtualDisplays on VR, AA (and everything else). + mTapDetector = new TaskTapPointerEventListener(mWmService, this); + registerPointerEventListener(mTapDetector); + } + if (mWmService.mMousePositionTracker != null) { + registerPointerEventListener(mWmService.mMousePositionTracker); + } if (mWmService.mAtmService.getRecentTasks() != null) { registerPointerEventListener( mWmService.mAtmService.getRecentTasks().getInputListener()); @@ -3260,6 +3273,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateTouchExcludeRegion() { + if (mTapDetector == null) { + // The touch exclude region is used to detect the region outside of the focused task + // so that the tap detector can detect outside touches. Don't calculate the exclude + // region when the tap detector is disabled. + return; + } final Task focusedTask = (mFocusedApp != null ? mFocusedApp.getTask() : null); if (focusedTask == null) { mTouchExcludeRegion.setEmpty(); @@ -3298,6 +3317,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private void processTaskForTouchExcludeRegion(Task task, Task focusedTask, int delta) { + if (mTapDetector == null) { + // The touch exclude region is used to detect the region outside of the focused task + // so that the tap detector can detect outside touches. Don't calculate the exclude + // region when the tap detector is disabled. + } final ActivityRecord topVisibleActivity = task.getTopVisibleActivity(); if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) { @@ -5502,6 +5526,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp configureDisplayPolicy(); } + if (!isDefaultDisplay) { + mDisplayRotation.updateRotationUnchecked(true); + } + reconfigureDisplayLocked(); onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration()); mWmService.mDisplayNotificationController.dispatchDisplayAdded(this); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index b862d7c28b52..460a68f48ff6 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCRE import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; @@ -272,6 +273,8 @@ public class DisplayPolicy { private @InsetsType int mForciblyShownTypes; + private boolean mImeInsetsConsumed; + private boolean mIsImmersiveMode; // The windows we were told about in focusChanged. @@ -1420,6 +1423,7 @@ public class DisplayPolicy { mShowingDream = false; mIsFreeformWindowOverlappingWithNavBar = false; mForciblyShownTypes = 0; + mImeInsetsConsumed = false; } /** @@ -1481,6 +1485,17 @@ public class DisplayPolicy { mForciblyShownTypes |= win.mAttrs.forciblyShownTypes; } + if (win.mImeInsetsConsumed != mImeInsetsConsumed) { + win.mImeInsetsConsumed = mImeInsetsConsumed; + final WindowState imeWin = mDisplayContent.mInputMethodWindow; + if (win.isReadyToDispatchInsetsState() && imeWin != null && imeWin.isVisible()) { + win.notifyInsetsChanged(); + } + } + if ((attrs.privateFlags & PRIVATE_FLAG_CONSUME_IME_INSETS) != 0 && win.isVisible()) { + mImeInsetsConsumed = true; + } + if (!affectsSystemUi) { return; } @@ -2828,6 +2843,7 @@ public class DisplayPolicy { } } pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen); + pw.print(prefix); pw.print("mImeInsetsConsumed="); pw.println(mImeInsetsConsumed); pw.print(prefix); pw.print("mForceShowNavigationBarEnabled="); pw.print(mForceShowNavigationBarEnabled); pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 14912d041127..bf30af3e8596 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -461,7 +461,7 @@ final class InputMonitor { // in animating before the next app window focused, or IME icon // persists on the bottom when swiping the task to recents. InputMethodManagerInternal.get().updateImeWindowStatus( - true /* disableImeIcon */); + true /* disableImeIcon */, mDisplayContent.getDisplayId()); } } return; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index c089d107d07d..781567990235 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -391,13 +391,26 @@ class InsetsPolicy { if (originalImeSource != null) { final boolean imeVisibility = w.isRequestedVisible(Type.ime()); - final InsetsState state = copyState ? new InsetsState(originalState) + final InsetsState state = copyState + ? new InsetsState(originalState) : originalState; final InsetsSource imeSource = new InsetsSource(originalImeSource); imeSource.setVisible(imeVisibility); state.addSource(imeSource); return state; } + } else if (w.mImeInsetsConsumed) { + // Set the IME source (if there is one) to be invisible if it has been consumed. + final InsetsSource originalImeSource = originalState.peekSource(ID_IME); + if (originalImeSource != null && originalImeSource.isVisible()) { + final InsetsState state = copyState + ? new InsetsState(originalState) + : originalState; + final InsetsSource imeSource = new InsetsSource(originalImeSource); + imeSource.setVisible(false); + state.addSource(imeSource); + return state; + } } return originalState; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index ccaa3b07aaaa..cbc7b836d250 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -240,7 +240,8 @@ class KeyguardController { // state when evaluating visibilities. updateKeyguardSleepToken(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); - InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */); + InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */, + displayId); setWakeTransitionReady(); if (aodChanged) { // Ensure the new state takes effect. diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index ef2572665281..dd538deee5cd 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -967,7 +967,8 @@ public class RecentsAnimationController implements DeathRecipient { // Restore IME icon only when moving the original app task to front from recents, in case // IME icon may missing if the moving task has already been the current focused task. if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) { - InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */); + InputMethodManagerInternal.get().updateImeWindowStatus( + false /* disableImeIcon */, mDisplayId); } // Update the input windows after the animation is complete diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 23c135a7b83a..03574029c061 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -26,6 +26,7 @@ import android.view.Display; import android.view.Display.Mode; import android.view.DisplayInfo; import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceControl.RefreshRateRange; import java.util.HashMap; @@ -191,26 +192,35 @@ class RefreshRatePolicy { public static class FrameRateVote { float mRefreshRate; @Surface.FrameRateCompatibility int mCompatibility; + @SurfaceControl.FrameRateSelectionStrategy int mSelectionStrategy; - FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility) { - update(refreshRate, compatibility); + + + FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility, + @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { + update(refreshRate, compatibility, selectionStrategy); } FrameRateVote() { reset(); } - boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility) { - if (!refreshRateEquals(refreshRate) || mCompatibility != compatibility) { + boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility, + @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) { + if (!refreshRateEquals(refreshRate) + || mCompatibility != compatibility + || mSelectionStrategy != selectionStrategy) { mRefreshRate = refreshRate; mCompatibility = compatibility; + mSelectionStrategy = selectionStrategy; return true; } return false; } boolean reset() { - return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE); } @Override @@ -221,17 +231,20 @@ class RefreshRatePolicy { FrameRateVote other = (FrameRateVote) o; return refreshRateEquals(other.mRefreshRate) - && mCompatibility == other.mCompatibility; + && mCompatibility == other.mCompatibility + && mSelectionStrategy == other.mSelectionStrategy; } @Override public int hashCode() { - return Objects.hash(mRefreshRate, mCompatibility); + return Objects.hash(mRefreshRate, mCompatibility, mSelectionStrategy); + } @Override public String toString() { - return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility; + return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility + + ", mSelectionStrategy=" + mSelectionStrategy; } private boolean refreshRateEquals(float refreshRate) { @@ -265,7 +278,8 @@ class RefreshRatePolicy { for (Display.Mode mode : mDisplayInfo.supportedModes) { if (preferredModeId == mode.getModeId()) { return w.mFrameRateVote.update(mode.getRefreshRate(), - Surface.FRAME_RATE_COMPATIBILITY_EXACT); + Surface.FRAME_RATE_COMPATIBILITY_EXACT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); } } } @@ -273,7 +287,8 @@ class RefreshRatePolicy { if (w.mAttrs.preferredRefreshRate > 0) { return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate, - Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); } // If the app didn't set a preferred mode id or refresh rate, but it is part of the deny @@ -282,7 +297,8 @@ class RefreshRatePolicy { final String packageName = w.getOwningPackage(); if (mHighRefreshRateDenylist.isDenylisted(packageName)) { return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(), - Surface.FRAME_RATE_COMPATIBILITY_EXACT); + Surface.FRAME_RATE_COMPATIBILITY_EXACT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); } } diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index 7d22b744ad5f..ac244c7b048e 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -45,6 +45,10 @@ public class TaskTapPointerEventListener implements PointerEventListener { public TaskTapPointerEventListener(WindowManagerService service, DisplayContent displayContent) { + // TODO(b/315321016): Remove this class when the flag rollout is complete. + if (com.android.input.flags.Flags.removePointerEventTrackingInWm()) { + throw new IllegalStateException("TaskTapPointerEventListener should not be used!"); + } mService = service; mDisplayContent = displayContent; } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 424394872821..76c4a0ee438b 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1390,7 +1390,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // recents, in case IME icon may missing if the moving task has already been // the current focused task. InputMethodManagerInternal.get().updateImeWindowStatus( - false /* disableImeIcon */); + false /* disableImeIcon */, dc.getDisplayId()); } // An uncommitted transient launch can leave incomplete lifecycles if visibilities // didn't change (eg. re-ordering with translucent tasks will leave launcher diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java new file mode 100644 index 000000000000..1688a1a91114 --- /dev/null +++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java @@ -0,0 +1,448 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.graphics.Matrix.MSCALE_X; +import static android.graphics.Matrix.MSCALE_Y; +import static android.graphics.Matrix.MSKEW_X; +import static android.graphics.Matrix.MSKEW_Y; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TPL; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.IntArray; +import android.util.Pair; +import android.util.Size; +import android.view.InputWindowHandle; +import android.window.ITrustedPresentationListener; +import android.window.TrustedPresentationThresholds; +import android.window.WindowInfosListener; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.server.wm.utils.RegionUtils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Optional; + +/** + * Class to handle TrustedPresentationListener registrations in a thread safe manner. This class + * also takes care of cleaning up listeners when the remote process dies. + */ +public class TrustedPresentationListenerController { + + // Should only be accessed by the posting to the handler + private class Listeners { + private final class ListenerDeathRecipient implements IBinder.DeathRecipient { + IBinder mListenerBinder; + int mInstances; + + ListenerDeathRecipient(IBinder listenerBinder) { + mListenerBinder = listenerBinder; + mInstances = 0; + try { + mListenerBinder.linkToDeath(this, 0); + } catch (RemoteException ignore) { + } + } + + void addInstance() { + mInstances++; + } + + // return true if there are no instances alive + boolean removeInstance() { + mInstances--; + if (mInstances > 0) { + return false; + } + mListenerBinder.unlinkToDeath(this, 0); + return true; + } + + public void binderDied() { + mHandler.post(() -> { + mUniqueListeners.remove(mListenerBinder); + removeListeners(mListenerBinder, Optional.empty()); + }); + } + } + + // tracks binder deaths for cleanup + ArrayMap<IBinder, ListenerDeathRecipient> mUniqueListeners = new ArrayMap<>(); + ArrayMap<IBinder /*window*/, ArrayList<TrustedPresentationInfo>> mWindowToListeners = + new ArrayMap<>(); + + void register(IBinder window, ITrustedPresentationListener listener, + TrustedPresentationThresholds thresholds, int id) { + var listenersForWindow = mWindowToListeners.computeIfAbsent(window, + iBinder -> new ArrayList<>()); + listenersForWindow.add(new TrustedPresentationInfo(thresholds, id, listener)); + + // register death listener + var listenerBinder = listener.asBinder(); + var deathRecipient = mUniqueListeners.computeIfAbsent(listenerBinder, + ListenerDeathRecipient::new); + deathRecipient.addInstance(); + } + + void unregister(ITrustedPresentationListener trustedPresentationListener, int id) { + var listenerBinder = trustedPresentationListener.asBinder(); + var deathRecipient = mUniqueListeners.get(listenerBinder); + if (deathRecipient == null) { + ProtoLog.e(WM_DEBUG_TPL, "unregister failed, couldn't find" + + " deathRecipient for %s with id=%d", trustedPresentationListener, id); + return; + } + + if (deathRecipient.removeInstance()) { + mUniqueListeners.remove(listenerBinder); + } + removeListeners(listenerBinder, Optional.of(id)); + } + + boolean isEmpty() { + return mWindowToListeners.isEmpty(); + } + + ArrayList<TrustedPresentationInfo> get(IBinder windowToken) { + return mWindowToListeners.get(windowToken); + } + + private void removeListeners(IBinder listenerBinder, Optional<Integer> id) { + for (int i = mWindowToListeners.size() - 1; i >= 0; i--) { + var listeners = mWindowToListeners.valueAt(i); + for (int j = listeners.size() - 1; j >= 0; j--) { + var listener = listeners.get(j); + if (listener.mListener.asBinder() == listenerBinder && (id.isEmpty() + || listener.mId == id.get())) { + listeners.remove(j); + } + } + if (listeners.isEmpty()) { + mWindowToListeners.removeAt(i); + } + } + } + } + + private final Object mHandlerThreadLock = new Object(); + private HandlerThread mHandlerThread; + private Handler mHandler; + + private WindowInfosListener mWindowInfosListener; + + Listeners mRegisteredListeners = new Listeners(); + + private InputWindowHandle[] mLastWindowHandles; + + private final Object mIgnoredWindowTokensLock = new Object(); + + private final ArraySet<IBinder> mIgnoredWindowTokens = new ArraySet<>(); + + private void startHandlerThreadIfNeeded() { + synchronized (mHandlerThreadLock) { + if (mHandler == null) { + mHandlerThread = new HandlerThread("WindowInfosListenerForTpl"); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + } + } + } + + void addIgnoredWindowTokens(IBinder token) { + synchronized (mIgnoredWindowTokensLock) { + mIgnoredWindowTokens.add(token); + } + } + + void removeIgnoredWindowTokens(IBinder token) { + synchronized (mIgnoredWindowTokensLock) { + mIgnoredWindowTokens.remove(token); + } + } + + void registerListener(IBinder window, ITrustedPresentationListener listener, + TrustedPresentationThresholds thresholds, int id) { + startHandlerThreadIfNeeded(); + mHandler.post(() -> { + ProtoLog.d(WM_DEBUG_TPL, "Registering listener=%s with id=%d for window=%s with %s", + listener, id, window, thresholds); + + mRegisteredListeners.register(window, listener, thresholds, id); + registerWindowInfosListener(); + // Update the initial state for the new registered listener + computeTpl(mLastWindowHandles); + }); + } + + void unregisterListener(ITrustedPresentationListener listener, int id) { + startHandlerThreadIfNeeded(); + mHandler.post(() -> { + ProtoLog.d(WM_DEBUG_TPL, "Unregistering listener=%s with id=%d", + listener, id); + + mRegisteredListeners.unregister(listener, id); + if (mRegisteredListeners.isEmpty()) { + unregisterWindowInfosListener(); + } + }); + } + + void dump(PrintWriter pw) { + final String innerPrefix = " "; + pw.println("TrustedPresentationListenerController:"); + pw.println(innerPrefix + "Active unique listeners (" + + mRegisteredListeners.mUniqueListeners.size() + "):"); + for (int i = 0; i < mRegisteredListeners.mWindowToListeners.size(); i++) { + pw.println( + innerPrefix + " window=" + mRegisteredListeners.mWindowToListeners.keyAt(i)); + final var listeners = mRegisteredListeners.mWindowToListeners.valueAt(i); + for (int j = 0; j < listeners.size(); j++) { + final var listener = listeners.get(j); + pw.println(innerPrefix + innerPrefix + " listener=" + listener.mListener.asBinder() + + " id=" + listener.mId + + " thresholds=" + listener.mThresholds); + } + } + } + + private void registerWindowInfosListener() { + if (mWindowInfosListener != null) { + return; + } + + mWindowInfosListener = new WindowInfosListener() { + @Override + public void onWindowInfosChanged(InputWindowHandle[] windowHandles, + DisplayInfo[] displayInfos) { + mHandler.post(() -> computeTpl(windowHandles)); + } + }; + mLastWindowHandles = mWindowInfosListener.register().first; + } + + private void unregisterWindowInfosListener() { + if (mWindowInfosListener == null) { + return; + } + + mWindowInfosListener.unregister(); + mWindowInfosListener = null; + mLastWindowHandles = null; + } + + private void computeTpl(InputWindowHandle[] windowHandles) { + mLastWindowHandles = windowHandles; + if (mLastWindowHandles == null || mLastWindowHandles.length == 0 + || mRegisteredListeners.isEmpty()) { + return; + } + + Rect tmpRect = new Rect(); + Matrix tmpInverseMatrix = new Matrix(); + float[] tmpMatrix = new float[9]; + Region coveredRegionsAbove = new Region(); + long currTimeMs = System.currentTimeMillis(); + ProtoLog.v(WM_DEBUG_TPL, "Checking %d windows", mLastWindowHandles.length); + + ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates = + new ArrayMap<>(); + ArraySet<IBinder> ignoredWindowTokens; + synchronized (mIgnoredWindowTokensLock) { + ignoredWindowTokens = new ArraySet<>(mIgnoredWindowTokens); + } + for (var windowHandle : mLastWindowHandles) { + if (ignoredWindowTokens.contains(windowHandle.getWindowToken())) { + ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name); + continue; + } + tmpRect.set(windowHandle.frame); + var listeners = mRegisteredListeners.get(windowHandle.getWindowToken()); + if (listeners != null) { + Region region = new Region(); + region.op(tmpRect, coveredRegionsAbove, Region.Op.DIFFERENCE); + windowHandle.transform.invert(tmpInverseMatrix); + tmpInverseMatrix.getValues(tmpMatrix); + float scaleX = (float) Math.sqrt(tmpMatrix[MSCALE_X] * tmpMatrix[MSCALE_X] + + tmpMatrix[MSKEW_X] * tmpMatrix[MSKEW_X]); + float scaleY = (float) Math.sqrt(tmpMatrix[MSCALE_Y] * tmpMatrix[MSCALE_Y] + + tmpMatrix[MSKEW_Y] * tmpMatrix[MSKEW_Y]); + + float fractionRendered = computeFractionRendered(region, new RectF(tmpRect), + windowHandle.contentSize, + scaleX, scaleY); + + checkIfInThreshold(listeners, listenerUpdates, fractionRendered, windowHandle.alpha, + currTimeMs); + } + + coveredRegionsAbove.op(tmpRect, Region.Op.UNION); + ProtoLog.v(WM_DEBUG_TPL, "coveredRegionsAbove updated with %s frame:%s region:%s", + windowHandle.name, tmpRect.toShortString(), coveredRegionsAbove); + } + + for (int i = 0; i < listenerUpdates.size(); i++) { + var updates = listenerUpdates.valueAt(i); + var listener = listenerUpdates.keyAt(i); + try { + listener.onTrustedPresentationChanged(updates.first.toArray(), + updates.second.toArray()); + } catch (RemoteException ignore) { + } + } + } + + private void addListenerUpdate( + ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates, + ITrustedPresentationListener listener, int id, boolean presentationState) { + var updates = listenerUpdates.get(listener); + if (updates == null) { + updates = new Pair<>(new IntArray(), new IntArray()); + listenerUpdates.put(listener, updates); + } + if (presentationState) { + updates.first.add(id); + } else { + updates.second.add(id); + } + } + + + private void checkIfInThreshold( + ArrayList<TrustedPresentationInfo> listeners, + ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates, + float fractionRendered, float alpha, long currTimeMs) { + ProtoLog.v(WM_DEBUG_TPL, "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d", + fractionRendered, alpha, currTimeMs); + for (int i = 0; i < listeners.size(); i++) { + var trustedPresentationInfo = listeners.get(i); + var listener = trustedPresentationInfo.mListener; + boolean lastState = trustedPresentationInfo.mLastComputedTrustedPresentationState; + boolean newState = + (alpha >= trustedPresentationInfo.mThresholds.minAlpha) && (fractionRendered + >= trustedPresentationInfo.mThresholds.minFractionRendered); + trustedPresentationInfo.mLastComputedTrustedPresentationState = newState; + + ProtoLog.v(WM_DEBUG_TPL, + "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f " + + "minFractionRendered=%f", + lastState, newState, alpha, trustedPresentationInfo.mThresholds.minAlpha, + fractionRendered, trustedPresentationInfo.mThresholds.minFractionRendered); + + if (lastState && !newState) { + // We were in the trusted presentation state, but now we left it, + // emit the callback if needed + if (trustedPresentationInfo.mLastReportedTrustedPresentationState) { + trustedPresentationInfo.mLastReportedTrustedPresentationState = false; + addListenerUpdate(listenerUpdates, listener, + trustedPresentationInfo.mId, /*presentationState*/ false); + ProtoLog.d(WM_DEBUG_TPL, "Adding untrusted state listener=%s with id=%d", + listener, trustedPresentationInfo.mId); + } + // Reset the timer + trustedPresentationInfo.mEnteredTrustedPresentationStateTime = -1; + } else if (!lastState && newState) { + // We were not in the trusted presentation state, but we entered it, begin the timer + // and make sure this gets called at least once more! + trustedPresentationInfo.mEnteredTrustedPresentationStateTime = currTimeMs; + mHandler.postDelayed(() -> { + computeTpl(mLastWindowHandles); + }, (long) (trustedPresentationInfo.mThresholds.stabilityRequirementMs * 1.5)); + } + + // Has the timer elapsed, but we are still in the state? Emit a callback if needed + if (!trustedPresentationInfo.mLastReportedTrustedPresentationState && newState && ( + currTimeMs - trustedPresentationInfo.mEnteredTrustedPresentationStateTime + > trustedPresentationInfo.mThresholds.stabilityRequirementMs)) { + trustedPresentationInfo.mLastReportedTrustedPresentationState = true; + addListenerUpdate(listenerUpdates, listener, + trustedPresentationInfo.mId, /*presentationState*/ true); + ProtoLog.d(WM_DEBUG_TPL, "Adding trusted state listener=%s with id=%d", + listener, trustedPresentationInfo.mId); + } + } + } + + private float computeFractionRendered(Region visibleRegion, RectF screenBounds, + Size contentSize, + float sx, float sy) { + ProtoLog.v(WM_DEBUG_TPL, + "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s " + + "scale=%f,%f", + visibleRegion, screenBounds, contentSize, sx, sy); + + if (contentSize.getWidth() == 0 || contentSize.getHeight() == 0) { + return -1; + } + if (screenBounds.width() == 0 || screenBounds.height() == 0) { + return -1; + } + + float fractionRendered = Math.min(sx * sy, 1.0f); + ProtoLog.v(WM_DEBUG_TPL, "fractionRendered scale=%f", fractionRendered); + + float boundsOverSourceW = screenBounds.width() / (float) contentSize.getWidth(); + float boundsOverSourceH = screenBounds.height() / (float) contentSize.getHeight(); + fractionRendered *= boundsOverSourceW * boundsOverSourceH; + ProtoLog.v(WM_DEBUG_TPL, "fractionRendered boundsOverSource=%f", fractionRendered); + // Compute the size of all the rects since they may be disconnected. + float[] visibleSize = new float[1]; + RegionUtils.forEachRect(visibleRegion, rect -> { + float size = rect.width() * rect.height(); + visibleSize[0] += size; + }); + + fractionRendered *= visibleSize[0] / (screenBounds.width() * screenBounds.height()); + return fractionRendered; + } + + private static class TrustedPresentationInfo { + boolean mLastComputedTrustedPresentationState = false; + boolean mLastReportedTrustedPresentationState = false; + long mEnteredTrustedPresentationStateTime = -1; + final TrustedPresentationThresholds mThresholds; + + final ITrustedPresentationListener mListener; + final int mId; + + private TrustedPresentationInfo(TrustedPresentationThresholds thresholds, int id, + ITrustedPresentationListener listener) { + mThresholds = thresholds; + mId = id; + mListener = listener; + checkValid(thresholds); + } + + private void checkValid(TrustedPresentationThresholds thresholds) { + if (thresholds.minAlpha <= 0 || thresholds.minFractionRendered <= 0 + || thresholds.stabilityRequirementMs < 1) { + throw new IllegalArgumentException( + "TrustedPresentationThresholds values are invalid"); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0d2c94d103fb..10dd334ed50c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -303,9 +303,11 @@ import android.window.AddToSurfaceSyncGroupResult; import android.window.ClientWindowFrames; import android.window.ISurfaceSyncGroupCompletedListener; import android.window.ITaskFpsCallback; +import android.window.ITrustedPresentationListener; import android.window.ScreenCapture; import android.window.SystemPerformanceHinter; import android.window.TaskSnapshot; +import android.window.TrustedPresentationThresholds; import android.window.WindowContainerToken; import android.window.WindowContextInfo; @@ -764,6 +766,9 @@ public class WindowManagerService extends IWindowManager.Stub private final SurfaceSyncGroupController mSurfaceSyncGroupController = new SurfaceSyncGroupController(); + final TrustedPresentationListenerController mTrustedPresentationListenerController = + new TrustedPresentationListenerController(); + @VisibleForTesting final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = @@ -7076,7 +7081,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } else if ("containers".equals(cmd)) { synchronized (mGlobalLock) { - mRoot.dumpChildrenNames(pw, " "); + mRoot.dumpChildrenNames(pw, ""); pw.println(" "); mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */); } @@ -7171,6 +7176,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); } mSystemPerformanceHinter.dump(pw, ""); + mTrustedPresentationListenerController.dump(pw); } } @@ -7302,7 +7308,12 @@ public class WindowManagerService extends IWindowManager.Stub } } - MousePositionTracker mMousePositionTracker = new MousePositionTracker(); + // The mouse position tracker will be obsolete after the Pointer Icon Refactor. + // TODO(b/293587049): Remove after the refactoring is fully rolled out. + @Nullable + final MousePositionTracker mMousePositionTracker = + com.android.input.flags.Flags.enablePointerChoreographer() ? null + : new MousePositionTracker(); private static class MousePositionTracker implements PointerEventListener { private boolean mLatestEventWasMouse; @@ -7354,6 +7365,9 @@ public class WindowManagerService extends IWindowManager.Stub }; void updatePointerIcon(IWindow client) { + if (mMousePositionTracker == null) { + return; + } int pointerDisplayId; float mouseX, mouseY; @@ -7400,6 +7414,9 @@ public class WindowManagerService extends IWindowManager.Stub } void restorePointerIconLocked(DisplayContent displayContent, float latestX, float latestY) { + if (mMousePositionTracker == null) { + return; + } // Mouse position tracker has not been getting updates while dragging, update it now. if (!mMousePositionTracker.updatePosition( displayContent.getDisplayId(), latestX, latestY)) { @@ -7423,6 +7440,9 @@ public class WindowManagerService extends IWindowManager.Stub } } void setMousePointerDisplayId(int displayId) { + if (mMousePositionTracker == null) { + return; + } mMousePositionTracker.setPointerDisplayId(displayId); } @@ -9771,4 +9791,17 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } } + + @Override + public void registerTrustedPresentationListener(IBinder window, + ITrustedPresentationListener listener, + TrustedPresentationThresholds thresholds, int id) { + mTrustedPresentationListenerController.registerListener(window, listener, thresholds, id); + } + + @Override + public void unregisterTrustedPresentationListener(ITrustedPresentationListener listener, + int id) { + mTrustedPresentationListenerController.unregisterListener(listener, id); + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e1f1f662c5aa..4e9d23c88db4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -178,6 +178,7 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; +import static com.android.window.flags.Flags.explicitRefreshRateHints; import static com.android.window.flags.Flags.secureWindowState; import static com.android.window.flags.Flags.surfaceTrustedOverlay; @@ -668,6 +669,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mSeamlesslyRotated = false; /** + * Whether the IME insets have been consumed. If {@code true}, this window won't be able to + * receive visible IME insets; {@code false}, otherwise. + */ + boolean mImeInsetsConsumed = false; + + /** * The insets state of sources provided by windows above the current window. */ final InsetsState mAboveInsetsState = new InsetsState(); @@ -1189,6 +1196,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow); parentWindow.addChild(this, sWindowSubLayerComparator); } + + if (token.mRoundedCornerOverlay) { + mWmService.mTrustedPresentationListenerController.addIgnoredWindowTokens( + getWindowToken()); + } } @Override @@ -1487,7 +1499,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (insetsChanged) { mWindowFrames.setInsetsChanged(false); - mWmService.mWindowsInsetsChanged--; + if (mWmService.mWindowsInsetsChanged > 0) { + mWmService.mWindowsInsetsChanged--; + } if (mWmService.mWindowsInsetsChanged == 0) { mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED); } @@ -2393,6 +2407,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } mWmService.postWindowRemoveCleanupLocked(this); + + mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens( + getWindowToken()); } @Override @@ -3796,13 +3813,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ void notifyInsetsChanged() { ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this); - mWindowFrames.setInsetsChanged(true); + if (!mWindowFrames.hasInsetsChanged()) { + mWindowFrames.setInsetsChanged(true); - // If the new InsetsState won't be dispatched before releasing WM lock, the following - // message will be executed. - mWmService.mWindowsInsetsChanged++; - mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED); - mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED); + // If the new InsetsState won't be dispatched before releasing WM lock, the following + // message will be executed. + mWmService.mWindowsInsetsChanged++; + mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED); + mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED); + } final WindowContainer p = getParent(); if (p != null) { @@ -4192,6 +4211,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } else { pw.print("null"); } + pw.println(); if (mXOffset != 0 || mYOffset != 0) { pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset); @@ -4225,6 +4245,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (computeDragResizing()) { pw.println(prefix + "computeDragResizing=" + computeDragResizing()); } + if (mImeInsetsConsumed) { + pw.println(prefix + "mImeInsetsConsumed=true"); + } pw.println(prefix + "isOnScreen=" + isOnScreen()); pw.println(prefix + "isVisible=" + isVisible()); pw.println(prefix + "keepClearAreas: restricted=" + mKeepClearAreas @@ -5185,9 +5208,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean voteChanged = refreshRatePolicy.updateFrameRateVote(this); if (voteChanged) { - getPendingTransaction().setFrameRate( - mSurfaceControl, mFrameRateVote.mRefreshRate, - mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS); + getPendingTransaction() + .setFrameRate(mSurfaceControl, mFrameRateVote.mRefreshRate, + mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS); + if (explicitRefreshRateHints()) { + getPendingTransaction().setFrameRateSelectionStrategy(mSurfaceControl, + mFrameRateVote.mSelectionStrategy); + } } } @@ -5622,9 +5649,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mKeyInterceptionInfo == null || mKeyInterceptionInfo.layoutParamsPrivateFlags != getAttrs().privateFlags || mKeyInterceptionInfo.layoutParamsType != getAttrs().type - || mKeyInterceptionInfo.windowTitle != getWindowTag()) { + || mKeyInterceptionInfo.windowTitle != getWindowTag() + || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()) { mKeyInterceptionInfo = new KeyInterceptionInfo(getAttrs().type, getAttrs().privateFlags, - getWindowTag().toString()); + getWindowTag().toString(), getOwningUid()); } return mKeyInterceptionInfo; } diff --git a/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java index 8c8f6a6cb386..193a0c8be60b 100644 --- a/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java +++ b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java @@ -30,7 +30,7 @@ public class DisplayInfoOverrides { * Set of DisplayInfo fields that are overridden in DisplayManager using values from * WindowManager */ - public static final DisplayInfoFields WM_OVERRIDE_FIELDS = (out, source) -> { + public static final DisplayInfoFieldsUpdater WM_OVERRIDE_FIELDS = (out, source) -> { out.appWidth = source.appWidth; out.appHeight = source.appHeight; out.smallestNominalAppWidth = source.smallestNominalAppWidth; @@ -55,7 +55,7 @@ public class DisplayInfoOverrides { public static void copyDisplayInfoFields(@NonNull DisplayInfo out, @NonNull DisplayInfo base, @Nullable DisplayInfo override, - @NonNull DisplayInfoFields fields) { + @NonNull DisplayInfoFieldsUpdater fields) { out.copyFrom(base); if (override != null) { @@ -66,7 +66,7 @@ public class DisplayInfoOverrides { /** * Callback interface that allows to specify a subset of fields of DisplayInfo object */ - public interface DisplayInfoFields { + public interface DisplayInfoFieldsUpdater { /** * Copies a subset of fields from {@param source} to {@param out} * diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 24ee16389fd2..b19f3d813985 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -187,7 +187,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.power.stats-V1-ndk", "android.hardware.thermal@1.0", - "android.hardware.thermal-V1-ndk", + "android.hardware.thermal-V2-ndk", "android.hardware.tv.input@1.0", "android.hardware.tv.input-V2-ndk", "android.hardware.vibrator-V2-cpp", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 0e45f61cbfe1..061fe0fc88d9 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -30,3 +30,6 @@ per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS + +# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java +per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS diff --git a/services/manifest_services.xml b/services/manifest_services.xml index e2fdfe9e8e47..76389154a885 100644 --- a/services/manifest_services.xml +++ b/services/manifest_services.xml @@ -4,14 +4,4 @@ <version>1</version> <fqname>IAltitudeService/default</fqname> </hal> - <hal format="aidl"> - <name>android.frameworks.vibrator</name> - <version>1</version> - <fqname>IVibratorController/default</fqname> - </hal> - <hal format="aidl"> - <name>android.frameworks.vibrator</name> - <version>1</version> - <fqname>IVibratorControlService/default</fqname> - </hal> </manifest> diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 39aaab25d7be..a212812b0768 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -31,6 +31,7 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; @@ -1396,16 +1397,20 @@ public class MidiService extends IMidiManager.Stub { XmlResourceParser parser = null; try { - parser = serviceInfo.loadXmlMetaData(mPackageManager, - MidiDeviceService.SERVICE_INTERFACE); - if (parser == null) return; + if (serviceInfo == null) { + Log.w(TAG, "Skipping null service info"); + return; + } // ignore virtual device servers that do not require the correct permission if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals( serviceInfo.permission)) { - Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName - + ": it does not require the permission " - + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE); + return; + } + parser = serviceInfo.loadXmlMetaData(mPackageManager, + MidiDeviceService.SERVICE_INTERFACE); + if (parser == null) { + Log.w(TAG, "loading xml metadata failed"); return; } @@ -1533,21 +1538,14 @@ public class MidiService extends IMidiManager.Stub { XmlResourceParser parser = null; try { - ComponentName componentName = new ComponentName(serviceInfo.packageName, - serviceInfo.name); - int resId = mPackageManager.getProperty(MidiUmpDeviceService.SERVICE_INTERFACE, - componentName).getResourceId(); - Resources resources = mPackageManager.getResourcesForApplication( - serviceInfo.packageName); - parser = resources.getXml(resId); - if (parser == null) return; + if (serviceInfo == null) { + Log.w(TAG, "Skipping null service info"); + return; + } // ignore virtual device servers that do not require the correct permission if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals( serviceInfo.permission)) { - Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName - + ": it does not require the permission " - + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE); return; } @@ -1557,6 +1555,31 @@ public class MidiService extends IMidiManager.Stub { return; } + ComponentName componentName = new ComponentName(serviceInfo.packageName, + serviceInfo.name); + Property property = mPackageManager.getProperty(MidiUmpDeviceService.SERVICE_INTERFACE, + componentName); + if (property == null) { + Log.w(TAG, "Getting MidiUmpDeviceService property failed"); + return; + } + int resId = property.getResourceId(); + if (resId == 0) { + Log.w(TAG, "Getting MidiUmpDeviceService resourceId failed"); + return; + } + Resources resources = mPackageManager.getResourcesForApplication( + serviceInfo.packageName); + if (resources == null) { + Log.w(TAG, "Getting resource failed " + serviceInfo.packageName); + return; + } + parser = resources.getXml(resId); + if (parser == null) { + Log.w(TAG, "Getting XML failed " + resId); + return; + } + Bundle properties = null; int numPorts = 0; boolean isPrivate = false; diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt index bb68bc5c791d..ea5fb5d62fad 100644 --- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt @@ -61,6 +61,38 @@ class DevicePermissionPolicy : SchemePolicy() { } } + fun MutateStateScope.removeInactiveDevicesPermission(activePersistentDeviceIds: Set<String>) { + newState.userStates.forEachIndexed { _, userId, userState -> + userState.appIdDevicePermissionFlags.forEachReversedIndexed { _, appId, _ -> + val appIdDevicePermissionFlags = + newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags() + val devicePermissionFlags = + appIdDevicePermissionFlags.mutate(appId) ?: return@forEachReversedIndexed + + val removePersistentDeviceIds = mutableSetOf<String>() + devicePermissionFlags.forEachIndexed { _, deviceId, _ -> + if (!activePersistentDeviceIds.contains(deviceId)) { + removePersistentDeviceIds.add(deviceId) + } + } + + removePersistentDeviceIds.forEach { deviceId -> devicePermissionFlags -= deviceId } + } + } + } + + fun MutateStateScope.onDeviceIdRemoved(deviceId: String) { + newState.userStates.forEachIndexed { _, userId, userState -> + userState.appIdDevicePermissionFlags.forEachReversedIndexed { _, appId, _ -> + val appIdDevicePermissionFlags = + newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags() + val devicePermissionFlags = + appIdDevicePermissionFlags.mutate(appId) ?: return@forEachReversedIndexed + devicePermissionFlags -= deviceId + } + } + } + override fun MutateStateScope.onStorageVolumeMounted( volumeUuid: String?, packageNames: List<String>, @@ -272,7 +304,7 @@ class DevicePermissionPolicy : SchemePolicy() { /** These permissions are supported for virtual devices. */ // TODO: b/298661870 - Use new API to get the list of device aware permissions. val DEVICE_AWARE_PERMISSIONS = - if (Flags.deviceAwarePermissionApis()) { + if (Flags.deviceAwarePermissionApisEnabled()) { setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) } else { emptySet<String>() diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 7c539502461b..0f65494cde3e 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -1551,7 +1551,8 @@ class PermissionService(private val service: AccessCheckingService) : permissionName: String, deviceId: Int, ): Int { - return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) { + return if (!Flags.deviceAwarePermissionApisEnabled() || + deviceId == Context.DEVICE_ID_DEFAULT) { with(policy) { getPermissionFlags(appId, userId, permissionName) } } else { if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) { @@ -1586,7 +1587,8 @@ class PermissionService(private val service: AccessCheckingService) : deviceId: Int, flags: Int ): Boolean { - return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) { + return if (!Flags.deviceAwarePermissionApisEnabled() || + deviceId == Context.DEVICE_ID_DEFAULT) { with(policy) { setPermissionFlags(appId, userId, permissionName, flags) } } else { if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) { @@ -2314,6 +2316,18 @@ class PermissionService(private val service: AccessCheckingService) : service.onSystemReady() virtualDeviceManagerInternal = LocalServices.getService(VirtualDeviceManagerInternal::class.java) + + virtualDeviceManagerInternal?.allPersistentDeviceIds?.let { persistentDeviceIds -> + service.mutateState { + with(devicePolicy) { removeInactiveDevicesPermission(persistentDeviceIds) } + } + } + + // trim permission states for the external devices, when they are removed. + virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { persistentDeviceId + -> + service.mutateState { with(devicePolicy) { onDeviceIdRemoved(persistentDeviceId) } } + } permissionControllerManager = PermissionControllerManager(context, PermissionThread.getHandler()) } @@ -2681,8 +2695,8 @@ class PermissionService(private val service: AccessCheckingService) : permissionName in NOTIFICATIONS_PERMISSIONS && runtimePermissionRevokedUids.get(uid, true) } - runtimePermissionChangedUidDevices - .getOrPut(uid) { mutableSetOf() } += persistentDeviceId + runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += + persistentDeviceId } if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) { @@ -2799,8 +2813,7 @@ class PermissionService(private val service: AccessCheckingService) : fun onPermissionsChanged(uid: Int, persistentDeviceId: String) { if (listeners.registeredCallbackCount > 0) { - obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId) - .sendToTarget() + obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId).sendToTarget() } } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index a0dc2b68415c..f07e820bf8b3 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -817,12 +817,14 @@ public class PackageManagerSettingsTests { ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" }); ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 }); + ps1.setUsesSdkLibrariesOptional(new boolean[] {true}); ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true)); ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" }); ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 }); + ps2.setUsesSdkLibrariesOptional(new boolean[] {false}); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); settingsUnderTest.writeLPr(computer, /*sync=*/true); @@ -838,19 +840,30 @@ public class PackageManagerSettingsTests { Truth.assertThat(readPs1).isNotNull(); Truth.assertThat(readPs1.getUsesSdkLibraries()).isNotNull(); Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).isNotNull(); + Truth.assertThat(readPs1.getUsesSdkLibrariesOptional()).isNotNull(); Truth.assertThat(readPs2).isNotNull(); Truth.assertThat(readPs2.getUsesSdkLibraries()).isNotNull(); Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).isNotNull(); + Truth.assertThat(readPs2.getUsesSdkLibrariesOptional()).isNotNull(); List<Long> ps1VersionsAsList = new ArrayList<>(); for (long version : ps1.getUsesSdkLibrariesVersionsMajor()) { ps1VersionsAsList.add(version); } + List<Boolean> ps1RequireAsList = new ArrayList<>(); + for (boolean optional : ps1.getUsesSdkLibrariesOptional()) { + ps1RequireAsList.add(optional); + } + List<Long> ps2VersionsAsList = new ArrayList<>(); for (long version : ps2.getUsesSdkLibrariesVersionsMajor()) { ps2VersionsAsList.add(version); } + List<Boolean> ps2RequireAsList = new ArrayList<>(); + for (boolean optional : ps2.getUsesSdkLibrariesOptional()) { + ps2RequireAsList.add(optional); + } Truth.assertThat(readPs1.getUsesSdkLibraries()).asList() .containsExactlyElementsIn(ps1.getUsesSdkLibraries()).inOrder(); @@ -858,11 +871,17 @@ public class PackageManagerSettingsTests { Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).asList() .containsExactlyElementsIn(ps1VersionsAsList).inOrder(); + Truth.assertThat(readPs1.getUsesSdkLibrariesOptional()).asList() + .containsExactlyElementsIn(ps1RequireAsList).inOrder(); + Truth.assertThat(readPs2.getUsesSdkLibraries()).asList() .containsExactlyElementsIn(ps2.getUsesSdkLibraries()).inOrder(); Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).asList() .containsExactlyElementsIn(ps2VersionsAsList).inOrder(); + + Truth.assertThat(readPs2.getUsesSdkLibrariesOptional()).asList() + .containsExactlyElementsIn(ps2RequireAsList).inOrder(); } @Test @@ -1047,6 +1066,7 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesSdkLibraries*/, null /*usesSdkLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, @@ -1087,6 +1107,7 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesSdkLibraries*/, null /*usesSdkLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, @@ -1129,6 +1150,7 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesSdkLibraries*/, null /*usesSdkLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, @@ -1167,6 +1189,7 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesSdkLibraries*/, null /*usesSdkLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, @@ -1214,6 +1237,7 @@ public class PackageManagerSettingsTests { UserManagerService.getInstance(), null /*usesSdkLibraries*/, null /*usesSdkLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, @@ -1263,6 +1287,7 @@ public class PackageManagerSettingsTests { null /*usesSdkLibrariesVersions*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*mimeGroups*/, UUID.randomUUID(), 34 /*targetSdkVersion*/, @@ -1311,6 +1336,7 @@ public class PackageManagerSettingsTests { null /*usesSdkLibrariesVersions*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*mimeGroups*/, UUID.randomUUID(), 34 /*targetSdkVersion*/, @@ -1356,6 +1382,7 @@ public class PackageManagerSettingsTests { null /*usesSdkLibrariesVersions*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, + null /*usesSdkLibrariesOptional*/, null /*mimeGroups*/, UUID.randomUUID(), 34 /*targetSdkVersion*/, diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java index ea88ec25b26e..a62cd4fc5fc2 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java @@ -1062,7 +1062,7 @@ public class PackageParserTest { .addProtectedBroadcast("foo8") .setSdkLibraryName("sdk12") .setSdkLibVersionMajor(42) - .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"}) + .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"}, true) .setStaticSharedLibraryName("foo23") .setStaticSharedLibraryVersion(100) .addUsesStaticLibrary("foo23", 100, new String[]{"digest"}) diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java index decb44c2cd9b..6202908cdfbf 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java @@ -599,8 +599,8 @@ public class ScanTests { .setVolumeUuid(UUID_ONE.toString()) .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"}) .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"}) - .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"}) - .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"}) + .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"}, false) + .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"}, true) .hideAsParsed()) .setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib") .setVersionCodeMajor(1) @@ -628,6 +628,7 @@ public class ScanTests { assertThat(pkgSetting.getUsesSdkLibraries(), arrayContaining("some.sdk.library", "some.other.sdk.library")); assertThat(pkgSetting.getUsesSdkLibrariesVersionsMajor(), is(new long[]{123L, 789L})); + assertThat(pkgSetting.getUsesSdkLibrariesOptional(), is(new boolean[]{false, true})); assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage)); assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName)))); assertThat(pkgSetting.getVersionCode(), diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index 170faf61858b..09b66c11d200 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -127,6 +127,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag "getUsesSdkLibraries", "getUsesSdkLibrariesVersionsMajor", "getUsesSdkLibrariesCertDigests", + "getUsesSdkLibrariesOptional", // Tested through addUsesStaticLibrary "addUsesStaticLibrary", "getUsesStaticLibraries", @@ -608,7 +609,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag .setSplitHasCode(1, false) .setSplitClassLoaderName(0, "testSplitClassLoaderNameZero") .setSplitClassLoaderName(1, "testSplitClassLoaderNameOne") - .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1")) + .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"), true) .addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2")) override fun finalizeObject(parcelable: Parcelable) { @@ -661,6 +662,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag expect.that(after.usesSdkLibrariesCertDigests!!.size).isEqualTo(1) expect.that(after.usesSdkLibrariesCertDigests!![0]).asList() .containsExactly("testCertDigest1") + expect.that(after.usesSdkLibrariesOptional).asList().containsExactly(true) expect.that(after.usesStaticLibraries).containsExactly("testStatic") expect.that(after.usesStaticLibrariesVersions).asList().containsExactly(3L) diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java index cdae8c6ab004..23886a17c890 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -167,6 +167,11 @@ public class BackgroundJobsControllerTest { } private void setStoppedState(int uid, String pkgName, boolean stopped) { + doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid); + sendPackageStoppedBroadcast(uid, pkgName, stopped); + } + + private void sendPackageStoppedBroadcast(int uid, String pkgName, boolean stopped) { Intent intent = new Intent( stopped ? Intent.ACTION_PACKAGE_RESTARTED : Intent.ACTION_PACKAGE_UNSTOPPED); intent.putExtra(Intent.EXTRA_UID, uid); @@ -174,14 +179,6 @@ public class BackgroundJobsControllerTest { mStoppedReceiver.onReceive(mContext, intent); } - private void setUidBias(int uid, int bias) { - int prevBias = mJobSchedulerService.getUidBias(uid); - doReturn(bias).when(mJobSchedulerService).getUidBias(uid); - synchronized (mBackgroundJobsController.mLock) { - mBackgroundJobsController.onUidBiasChangedLocked(uid, prevBias, bias); - } - } - private void trackJobs(JobStatus... jobs) { for (JobStatus job : jobs) { mJobStore.add(job); @@ -208,6 +205,47 @@ public class BackgroundJobsControllerTest { } @Test + public void testRestartedBroadcastWithoutStopping() { + mSetFlagsRule.enableFlags(android.content.pm.Flags.FLAG_STAY_STOPPED); + // Scheduled by SOURCE_UID:SOURCE_PACKAGE for itself. + JobStatus directJob1 = createJobStatus("testStopped", SOURCE_PACKAGE, SOURCE_UID, + createBaseJobInfoBuilder(SOURCE_PACKAGE, 1).build()); + // Scheduled by ALTERNATE_UID:ALTERNATE_SOURCE_PACKAGE for itself. + JobStatus directJob2 = createJobStatus("testStopped", + ALTERNATE_SOURCE_PACKAGE, ALTERNATE_UID, + createBaseJobInfoBuilder(ALTERNATE_SOURCE_PACKAGE, 2).build()); + // Scheduled by CALLING_PACKAGE for SOURCE_PACKAGE. + JobStatus proxyJob1 = createJobStatus("testStopped", SOURCE_PACKAGE, CALLING_UID, + createBaseJobInfoBuilder(CALLING_PACKAGE, 3).build()); + // Scheduled by CALLING_PACKAGE for ALTERNATE_SOURCE_PACKAGE. + JobStatus proxyJob2 = createJobStatus("testStopped", + ALTERNATE_SOURCE_PACKAGE, CALLING_UID, + createBaseJobInfoBuilder(CALLING_PACKAGE, 4).build()); + + trackJobs(directJob1, directJob2, proxyJob1, proxyJob2); + + sendPackageStoppedBroadcast(ALTERNATE_UID, ALTERNATE_SOURCE_PACKAGE, true); + assertTrue(directJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(directJob1.isUserBgRestricted()); + assertTrue(directJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(directJob2.isUserBgRestricted()); + assertTrue(proxyJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(proxyJob1.isUserBgRestricted()); + assertTrue(proxyJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(proxyJob2.isUserBgRestricted()); + + sendPackageStoppedBroadcast(ALTERNATE_UID, ALTERNATE_SOURCE_PACKAGE, false); + assertTrue(directJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(directJob1.isUserBgRestricted()); + assertTrue(directJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(directJob2.isUserBgRestricted()); + assertTrue(proxyJob1.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(proxyJob1.isUserBgRestricted()); + assertTrue(proxyJob2.isConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); + assertFalse(proxyJob2.isUserBgRestricted()); + } + + @Test public void testStopped_disabled() { mSetFlagsRule.disableFlags(android.content.pm.Flags.FLAG_STAY_STOPPED); // Scheduled by SOURCE_UID:SOURCE_PACKAGE for itself. diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java index 4fb9472021c5..650c473533ed 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java @@ -30,15 +30,16 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS; import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS; +import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_APPLIED_CONSTRAINTS; import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT; import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE; -import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED; import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS; -import static com.android.server.job.controllers.FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS; +import static com.android.server.job.controllers.FlexibilityController.FLEXIBLE_CONSTRAINTS; import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY; +import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS; @@ -66,6 +67,7 @@ import android.os.Looper; import android.provider.DeviceConfig; import android.util.ArraySet; +import com.android.server.AppSchedulingModuleThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; @@ -129,6 +131,7 @@ public class FlexibilityControllerTest { when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)).thenReturn(false); // Used in FlexibilityController.FcConstants. doAnswer((Answer<Void>) invocationOnMock -> null) .when(() -> DeviceConfig.addOnPropertiesChangedListener( @@ -161,7 +164,8 @@ public class FlexibilityControllerTest { setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,60,70,80"); setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L); - setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true); + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); + waitForQuietModuleThread(); } @After @@ -171,26 +175,22 @@ public class FlexibilityControllerTest { } } - private void setDeviceConfigBoolean(String key, boolean val) { - mDeviceConfigPropertiesBuilder.setBoolean(key, val); - synchronized (mFlexibilityController.mLock) { - mFlexibilityController.prepareForUpdatedConstantsLocked(); - mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); - mFlexibilityController.onConstantsUpdatedLocked(); - } + private void setDeviceConfigInt(String key, int val) { + mDeviceConfigPropertiesBuilder.setInt(key, val); + updateDeviceConfig(key); } private void setDeviceConfigLong(String key, Long val) { mDeviceConfigPropertiesBuilder.setLong(key, val); - synchronized (mFlexibilityController.mLock) { - mFlexibilityController.prepareForUpdatedConstantsLocked(); - mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); - mFlexibilityController.onConstantsUpdatedLocked(); - } + updateDeviceConfig(key); } private void setDeviceConfigString(String key, String val) { mDeviceConfigPropertiesBuilder.setString(key, val); + updateDeviceConfig(key); + } + + private void updateDeviceConfig(String key) { synchronized (mFlexibilityController.mLock) { mFlexibilityController.prepareForUpdatedConstantsLocked(); mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); @@ -198,6 +198,11 @@ public class FlexibilityControllerTest { } } + private void waitForQuietModuleThread() { + assertTrue("Failed to wait for quiet module thread", + AppSchedulingModuleThread.getHandler().runWithScissors(() -> {}, 10_000L)); + } + private static JobInfo.Builder createJob(int id) { return new JobInfo.Builder(id, new ComponentName("foo", "bar")); } @@ -207,6 +212,10 @@ public class FlexibilityControllerTest { JobStatus js = JobStatus.createFromJobInfo( jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, "FCTest", testTag); js.enqueueTime = FROZEN_TIME; + if (js.hasFlexibilityConstraint()) { + js.setNumAppliedFlexibleConstraints(Integer.bitCount( + mFlexibilityController.getRelevantAppliedConstraintsLocked(js))); + } return js; } @@ -215,18 +224,120 @@ public class FlexibilityControllerTest { */ @Test public void testDefaultVariableValues() { - assertEquals(NUM_FLEXIBLE_CONSTRAINTS, + assertEquals(Integer.bitCount(FLEXIBLE_CONSTRAINTS), mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length ); } @Test - public void testOnConstantsUpdated_DefaultFlexibility() { + public void testAppliedConstraints() { + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); + + // Add connectivity to require 4 constraints + JobStatus connJs = createJobStatus("testAppliedConstraints", + createJob(0).setRequiredNetworkType(NETWORK_TYPE_ANY)); + JobStatus nonConnJs = createJobStatus("testAppliedConstraints", + createJob(1).setRequiredNetworkType(NETWORK_TYPE_NONE)); + + mFlexibilityController.maybeStartTrackingJobLocked(connJs, null); + mFlexibilityController.maybeStartTrackingJobLocked(nonConnJs, null); + + assertEquals(4, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(3, nonConnJs.getNumAppliedFlexibleConstraints()); + + mFlexibilityController.setConstraintSatisfied( + CONSTRAINT_BATTERY_NOT_LOW, true, + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); + mFlexibilityController.setConstraintSatisfied( + CONSTRAINT_CHARGING, false, + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); + mFlexibilityController.setConstraintSatisfied( + CONSTRAINT_IDLE, false, + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); + mFlexibilityController.setConstraintSatisfied( + CONSTRAINT_CONNECTIVITY, true, + JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS); + connJs.setTransportAffinitiesSatisfied(true); + + synchronized (mFlexibilityController.mLock) { + assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, + CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_CONNECTIVITY); + waitForQuietModuleThread(); + + // Only battery-not-low (which is satisfied) applies to the non-connectivity job, so it + // should be able to run. + assertEquals(2, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_BATTERY_NOT_LOW); + waitForQuietModuleThread(); + + assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONNECTIVITY); + waitForQuietModuleThread(); + + // No constraints apply to the non-connectivity job, so it should be able to run. + assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CHARGING); + waitForQuietModuleThread(); + + assertEquals(1, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(1, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertFalse(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0); + waitForQuietModuleThread(); + + // No constraints apply, so they should be able to run. + assertEquals(0, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + + // Invalid constraint to apply. + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, CONSTRAINT_CONTENT_TRIGGER); + waitForQuietModuleThread(); + + // No constraints apply, so they should be able to run. + assertEquals(0, connJs.getNumAppliedFlexibleConstraints()); + assertEquals(0, nonConnJs.getNumAppliedFlexibleConstraints()); + synchronized (mFlexibilityController.mLock) { + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(connJs)); + assertTrue(mFlexibilityController.hasEnoughSatisfiedConstraintsLocked(nonConnJs)); + } + } + + @Test + public void testOnConstantsUpdated_AppliedConstraints() { JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0)); - assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); - setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false); + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, 0); assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); - setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true); + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); } @@ -261,10 +372,10 @@ public class FlexibilityControllerTest { new int[] {10, 20, 30, 40}); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(1); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 2, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(2); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 3, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); } @@ -303,12 +414,12 @@ public class FlexibilityControllerTest { .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, @@ -321,11 +432,11 @@ public class FlexibilityControllerTest { nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(130400100, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(156320100L, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(182240100L, nextTimeToDropNumConstraints); @@ -337,11 +448,11 @@ public class FlexibilityControllerTest { nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(129600100, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(155520100L, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(181440100L, nextTimeToDropNumConstraints); @@ -357,12 +468,12 @@ public class FlexibilityControllerTest { .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, nextTimeToDropNumConstraints); - js.adjustNumRequiredFlexibleConstraints(-1); + js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, @@ -580,10 +691,10 @@ public class FlexibilityControllerTest { } @Test - public void testWontStopJobFromRunning() { - JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101)); + public void testWontStopAlreadyRunningJob() { + JobStatus js = createJobStatus("testWontStopAlreadyRunningJob", createJob(101)); // Stop satisfied constraints from causing a false positive. - js.adjustNumRequiredFlexibleConstraints(100); + js.setNumAppliedFlexibleConstraints(100); synchronized (mFlexibilityController.mLock) { when(mJobSchedulerService.isCurrentlyRunningLocked(js)).thenReturn(true); assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); @@ -593,10 +704,9 @@ public class FlexibilityControllerTest { @Test public void testFlexibilityTracker() { FlexibilityController.FlexibilityTracker flexTracker = - mFlexibilityController.new - FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS); + mFlexibilityController.new FlexibilityTracker(4); // Plus one for jobs with 0 required constraint. - assertEquals(NUM_FLEXIBLE_CONSTRAINTS + 1, flexTracker.size()); + assertEquals(4 + 1, flexTracker.size()); JobStatus[] jobs = new JobStatus[4]; JobInfo.Builder jb; for (int i = 0; i < jobs.length; i++) { @@ -622,21 +732,21 @@ public class FlexibilityControllerTest { assertEquals(3, trackedJobs.get(3).size()); assertEquals(0, trackedJobs.get(4).size()); - flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME); + flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 1); assertEquals(1, trackedJobs.get(0).size()); assertEquals(0, trackedJobs.get(1).size()); assertEquals(1, trackedJobs.get(2).size()); assertEquals(2, trackedJobs.get(3).size()); assertEquals(0, trackedJobs.get(4).size()); - flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME); + flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2); assertEquals(1, trackedJobs.get(0).size()); assertEquals(1, trackedJobs.get(1).size()); assertEquals(0, trackedJobs.get(2).size()); assertEquals(2, trackedJobs.get(3).size()); assertEquals(0, trackedJobs.get(4).size()); - flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME); + flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 3); assertEquals(2, trackedJobs.get(0).size()); assertEquals(0, trackedJobs.get(1).size()); assertEquals(0, trackedJobs.get(2).size()); @@ -650,14 +760,14 @@ public class FlexibilityControllerTest { assertEquals(1, trackedJobs.get(3).size()); assertEquals(0, trackedJobs.get(4).size()); - flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME); + flexTracker.calculateNumDroppedConstraints(jobs[0], FROZEN_TIME); assertEquals(1, trackedJobs.get(0).size()); assertEquals(0, trackedJobs.get(1).size()); assertEquals(0, trackedJobs.get(2).size()); assertEquals(2, trackedJobs.get(3).size()); assertEquals(0, trackedJobs.get(4).size()); - flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME); + flexTracker.setNumDroppedFlexibleConstraints(jobs[0], 2); assertEquals(1, trackedJobs.get(0).size()); assertEquals(1, trackedJobs.get(1).size()); assertEquals(0, trackedJobs.get(2).size()); @@ -669,7 +779,7 @@ public class FlexibilityControllerTest { JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); - flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed); + flexTracker.calculateNumDroppedConstraints(jobs[0], nowElapsed); assertEquals(1, trackedJobs.get(0).size()); assertEquals(0, trackedJobs.get(1).size()); assertEquals(1, trackedJobs.get(2).size()); @@ -779,9 +889,9 @@ public class FlexibilityControllerTest { mFlexibilityController.setConstraintSatisfied( SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS, false, FROZEN_TIME); // Require only a single constraint - jsAny.adjustNumRequiredFlexibleConstraints(-3); - jsCell.adjustNumRequiredFlexibleConstraints(-2); - jsWifi.adjustNumRequiredFlexibleConstraints(-2); + jsAny.setNumAppliedFlexibleConstraints(1); + jsCell.setNumAppliedFlexibleConstraints(1); + jsWifi.setNumAppliedFlexibleConstraints(1); synchronized (mFlexibilityController.mLock) { jsAny.setTransportAffinitiesSatisfied(false); jsCell.setTransportAffinitiesSatisfied(false); @@ -1008,9 +1118,9 @@ public class FlexibilityControllerTest { } @Test - public void testResetJobNumDroppedConstraints() { + public void testCalculateNumDroppedConstraints() { JobInfo.Builder jb = createJob(22); - JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb); + JobStatus js = createJobStatus("testCalculateNumDroppedConstraints", jb); long nowElapsed = FROZEN_TIME; mFlexibilityController.mFlexibilityTracker.add(js); @@ -1025,14 +1135,14 @@ public class FlexibilityControllerTest { Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); mFlexibilityController.mFlexibilityTracker - .adjustJobsRequiredConstraints(js, -1, nowElapsed); + .setNumDroppedFlexibleConstraints(js, 1); assertEquals(2, js.getNumRequiredFlexibleConstraints()); assertEquals(1, js.getNumDroppedFlexibleConstraints()); assertEquals(1, mFlexibilityController .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); - mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); assertEquals(2, js.getNumRequiredFlexibleConstraints()); assertEquals(1, js.getNumDroppedFlexibleConstraints()); @@ -1043,7 +1153,7 @@ public class FlexibilityControllerTest { JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); - mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); assertEquals(3, js.getNumRequiredFlexibleConstraints()); assertEquals(0, js.getNumDroppedFlexibleConstraints()); @@ -1054,7 +1164,7 @@ public class FlexibilityControllerTest { JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); - mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); assertEquals(0, js.getNumRequiredFlexibleConstraints()); assertEquals(3, js.getNumDroppedFlexibleConstraints()); @@ -1063,7 +1173,7 @@ public class FlexibilityControllerTest { JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); - mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed); + mFlexibilityController.mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed); assertEquals(1, js.getNumRequiredFlexibleConstraints()); assertEquals(2, js.getNumDroppedFlexibleConstraints()); @@ -1139,20 +1249,28 @@ public class FlexibilityControllerTest { } @Test - public void testDeviceDisabledFlexibility_Auto() { - when(mPackageManager.hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true); + public void testUnsupportedDevice_Auto() { + runTestUnsupportedDevice(PackageManager.FEATURE_AUTOMOTIVE); + } + + @Test + public void testUnsupportedDevice_Embedded() { + runTestUnsupportedDevice(PackageManager.FEATURE_EMBEDDED); + } + + private void runTestUnsupportedDevice(String feature) { + when(mPackageManager.hasSystemFeature(feature)).thenReturn(true); mFlexibilityController = new FlexibilityController(mJobSchedulerService, mPrefetchController); - assertFalse(mFlexibilityController.mFlexibilityEnabled); + assertFalse(mFlexibilityController.isEnabled()); - JobStatus js = createJobStatus("testIsAuto", createJob(0)); + JobStatus js = createJobStatus("testUnsupportedDevice", createJob(0)); mFlexibilityController.maybeStartTrackingJobLocked(js, null); assertTrue(js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)); - setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true); - assertFalse(mFlexibilityController.mFlexibilityEnabled); + setDeviceConfigInt(KEY_APPLIED_CONSTRAINTS, FLEXIBLE_CONSTRAINTS); + assertFalse(mFlexibilityController.isEnabled()); ArrayList<ArraySet<JobStatus>> jobs = mFlexibilityController.mFlexibilityTracker.getArrayList(); 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 8397b87706d6..293391f43828 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 @@ -232,6 +232,19 @@ public class JobStatusTest { } @Test + public void testFlexibleConstraintCounts() { + JobStatus js = createJobStatus(new JobInfo.Builder(101, new ComponentName("foo", "bar")) + .setUserInitiated(false) + .build()); + + js.setNumAppliedFlexibleConstraints(3); + js.setNumDroppedFlexibleConstraints(2); + assertEquals(3, js.getNumAppliedFlexibleConstraints()); + assertEquals(2, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, js.getNumRequiredFlexibleConstraints()); + } + + @Test public void testIsUserVisibleJob() { JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar")) .setUserInitiated(false) 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 a250ac75635b..07027645411d 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 @@ -368,10 +368,15 @@ public class QuotaControllerTest { } } + private JobInfo.Builder createJobInfoBuilder(int jobId) { + return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestQuotaJobService")); + } + private JobStatus createJobStatus(String testTag, int jobId) { - JobInfo jobInfo = new JobInfo.Builder(jobId, - new ComponentName(mContext, "TestQuotaJobService")) - .build(); + return createJobStatus(testTag, createJobInfoBuilder(jobId).build()); + } + + private JobStatus createJobStatus(String testTag, JobInfo jobInfo) { return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo); } @@ -1333,39 +1338,70 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); + final long timeUntilQuotaConsumedMs = 7 * MINUTE_IN_MILLIS; JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); + //noinspection deprecation + JobStatus jobDefIWF = createJobStatus("testGetMaxJobExecutionTimeLocked", + createJobInfoBuilder(1) + .setImportantWhileForeground(true) + .setPriority(JobInfo.PRIORITY_DEFAULT) + .build()); + JobStatus jobHigh = createJobStatus("testGetMaxJobExecutionTimeLocked", + createJobInfoBuilder(2).setPriority(JobInfo.PRIORITY_HIGH).build()); setStandbyBucket(RARE_INDEX, job); + setStandbyBucket(RARE_INDEX, jobDefIWF); + setStandbyBucket(RARE_INDEX, jobHigh); setCharging(); synchronized (mQuotaController.mLock) { assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); } setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); synchronized (mQuotaController.mLock) { - assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); } // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job, null); + trackJobs(job, jobDefIWF, jobHigh); mQuotaController.prepareForExecutionLocked(job); + mQuotaController.prepareForExecutionLocked(jobDefIWF); + mQuotaController.prepareForExecutionLocked(jobHigh); } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { - assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF))); + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh))); mQuotaController.maybeStopTrackingJobLocked(job, null); + mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null); + mQuotaController.maybeStopTrackingJobLocked(jobHigh, null); } setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); synchronized (mQuotaController.mLock) { - assertEquals(7 * MINUTE_IN_MILLIS, + assertEquals(timeUntilQuotaConsumedMs, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF)); + assertEquals(timeUntilQuotaConsumedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh)); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt index cf81f0a77702..e131a98b52d0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt @@ -16,12 +16,14 @@ package com.android.server.pm +import android.app.AppOpsManager import android.content.Intent import android.content.pm.PackageManager import android.os.Binder import com.android.server.testutils.any import com.android.server.testutils.eq import com.android.server.testutils.nullable +import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -31,6 +33,7 @@ import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify + @RunWith(JUnit4::class) class DistractingPackageHelperTest : PackageHelperTestBase() { @@ -40,6 +43,9 @@ class DistractingPackageHelperTest : PackageHelperTestBase() { super.setup() distractingPackageHelper = DistractingPackageHelper( pms, broadcastHelper, suspendPackageHelper) + whenever(rule.mocks().appOpsManager.checkOpNoThrow( + eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION), any(), any())) + .thenReturn(AppOpsManager.MODE_DEFAULT) } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 46806f334a27..28bd98781ced 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -15,6 +15,7 @@ */ package com.android.server.pm +import android.app.AppOpsManager import android.app.PropertyInvalidatedCache import android.content.Context import android.content.Intent @@ -151,7 +152,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { .mockStatic(EventLog::class.java) .mockStatic(LocalServices::class.java) .mockStatic(LocalManagerRegistry::class.java) - .mockStatic(DeviceConfig::class.java) + .mockStatic(DeviceConfig::class.java, Mockito.CALLS_REAL_METHODS) .mockStatic(HexEncoding::class.java) .apply(withSession) session = apply.startMocking() @@ -192,6 +193,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val userManagerService: UserManagerService = mock() val componentResolver: ComponentResolver = mock() val permissionManagerInternal: PermissionManagerServiceInternal = mock() + val appOpsManager: AppOpsManager = mock() val incrementalManager: IncrementalManager = mock() val platformCompat: PlatformCompat = mock() val settings: Settings = mock() @@ -304,6 +306,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider } whenever(mocks.injector.backgroundHandler) { mocks.backgroundHandler } whenever(mocks.injector.updateOwnershipHelper) { mocks.updateOwnershipHelper } + whenever(mocks.injector.getSystemService(AppOpsManager::class.java)) { mocks.appOpsManager } wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP) whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST) diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 7b381ce443e1..147303363200 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -16,12 +16,14 @@ package com.android.server.pm +import android.app.AppOpsManager import android.content.Intent import android.content.pm.SuspendDialogInfo import android.os.Binder import android.os.PersistableBundle import com.android.server.testutils.any import com.android.server.testutils.eq +import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -32,6 +34,12 @@ import org.mockito.Mockito.verify @RunWith(JUnit4::class) class SuspendPackageHelperTest : PackageHelperTestBase() { + override fun setup() { + super.setup() + whenever(rule.mocks().appOpsManager.checkOpNoThrow( + eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION), any(), any())) + .thenReturn(AppOpsManager.MODE_DEFAULT) + } @Test fun setPackagesSuspended() { diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index f02e5a5ece24..07197b1ab9df 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -12,6 +12,7 @@ android_test { ], exclude_srcs: [ + "src/com/android/server/power/stats/MultiStateStatsTest.java", "src/com/android/server/power/stats/PowerStatsStoreTest.java", ], @@ -62,13 +63,12 @@ android_ravenwood_test { static_libs: [ "services.core", "modules-utils-binary-xml", - "androidx.annotation_annotation", "androidx.test.rules", ], srcs: [ + "src/com/android/server/power/stats/MultiStateStatsTest.java", "src/com/android/server/power/stats/PowerStatsStoreTest.java", ], - sdk_version: "test_current", auto_gen_config: true, } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java index 48e2dd74fcef..6d61dc8d31fa 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java @@ -26,8 +26,6 @@ import android.os.BatteryConsumer; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.os.MultiStateStats; - import org.junit.Test; import org.junit.runner.RunWith; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java index 8ca4ff6f86f5..993d834b9500 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java @@ -38,7 +38,6 @@ import android.util.LongArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.os.MultiStateStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java index eb03a6c14f7d..e8f46b30fb8c 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java @@ -22,12 +22,12 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import android.os.BatteryConsumer; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.os.MultiStateStats; - +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,6 +39,10 @@ import java.util.Arrays; @SmallTest public class MultiStateStatsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .build(); + public static final int DIMENSION_COUNT = 2; @Test diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 2ece8c74420c..9b8419021c5c 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -79,6 +79,7 @@ android_test { "coretests-aidl", "securebox", "flag-junit", + "ravenwood-junit", ], libs: [ @@ -140,6 +141,23 @@ android_test { resource_zips: [":FrameworksServicesTests_apks_as_resources"], } +android_ravenwood_test { + name: "FrameworksServicesTestsRavenwood", + libs: [ + "android.test.mock", + ], + static_libs: [ + "androidx.annotation_annotation", + "androidx.test.rules", + "mockito_ravenwood", + "services.core", + ], + srcs: [ + "src/com/android/server/uri/**/*.java", + ], + auto_gen_config: true, +} + java_library { name: "servicestests-core-utils", srcs: [ diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index e89199dc9278..6537d47106a9 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -40,11 +40,13 @@ mediaSharedWithParent='true' credentialShareableWithParent='false' authAlwaysRequiredToDisableQuietMode='true' + allowStoppingUserWithDelayedLocking='true' showInSettings='23' hideInSettingsInQuietMode='true' inheritDevicePolicy='450' deleteAppWithParent='false' alwaysVisible='true' + crossProfileContentSharingStrategy='0' /> </profile-type> <profile-type name='custom.test.1' max-allowed-per-parent='14' /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 91140276cde0..a9967f63b3b3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -717,6 +717,45 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void testSecondFingerSwipe_twoPointerDownAndActivatedState_shouldInPanningState() { + goFromStateIdleTo(STATE_ACTIVATED); + PointF pointer1 = DEFAULT_POINT; + PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y); + + send(downEvent()); + send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1)); + //The minimum movement to transit to panningState. + final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop(); + pointer2.offset(sWipeMinDistance + 1, 0); + send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1)); + fastForward(ViewConfiguration.getTapTimeout()); + assertIn(STATE_PANNING); + + returnToNormalFrom(STATE_PANNING); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void testTowFingerSwipe_twoPointerDownAndShortcutTriggeredState_shouldInPanningState() { + goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED); + PointF pointer1 = DEFAULT_POINT; + PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y); + + send(downEvent()); + send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1)); + //The minimum movement to transit to panningState. + final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop(); + pointer2.offset(sWipeMinDistance + 1, 0); + send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1)); + fastForward(ViewConfiguration.getTapTimeout()); + assertIn(STATE_PANNING); + + returnToNormalFrom(STATE_PANNING); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testSecondFingerSwipe_twoPointerDownAndActivatedState_panningState() { goFromStateIdleTo(STATE_ACTIVATED); PointF pointer1 = DEFAULT_POINT; @@ -734,6 +773,7 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testSecondFingerSwipe_twoPointerDownAndShortcutTriggeredState_panningState() { goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED); PointF pointer1 = DEFAULT_POINT; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java index 162d2a9d98af..d94faec4cf01 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java @@ -20,6 +20,7 @@ import static com.android.server.accessibility.utils.TouchEventGenerator.movePoi import static com.android.server.accessibility.utils.TouchEventGenerator.twoPointersDownEvents; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.after; import static org.mockito.Mockito.timeout; @@ -27,6 +28,10 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.PointF; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.Display; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -37,6 +42,7 @@ import com.android.server.accessibility.utils.TouchEventGenerator; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -48,6 +54,9 @@ import java.util.List; */ public class TwoFingersDownOrSwipeTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final float DEFAULT_X = 100f; private static final float DEFAULT_Y = 100f; @@ -85,7 +94,8 @@ public class TwoFingersDownOrSwipeTest { } @Test - public void sendTwoFingerDownEvent_onGestureCompleted() { + @RequiresFlagsDisabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION) + public void sendTwoFingerDownEvent_onGestureCompleted_withoutCopiedEvents() { final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY, new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10)); @@ -99,6 +109,23 @@ public class TwoFingersDownOrSwipeTest { } @Test + @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION) + public void sendTwoFingerDownEvent_onGestureCompleted() { + final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY, + new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10)); + + for (MotionEvent event : downEvents) { + mGesturesObserver.onMotionEvent(event, event, 0); + } + + verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted( + eq(MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE), + argThat(argument -> downEvents.get(1).getId() == argument.getId()), + argThat(argument -> downEvents.get(1).getId() == argument.getId()), + eq(0)); + } + + @Test public void sendSingleTapEvent_onGestureCancelled() { final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY, DEFAULT_X, DEFAULT_Y); diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index d26d67107001..77b1455a2ecc 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -92,6 +92,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import android.view.Display; @@ -104,11 +105,14 @@ import com.android.server.am.UserState.KeyEvictedCallback; import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; +import com.android.server.pm.UserTypeDetails; +import com.android.server.pm.UserTypeFactory; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerService; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -175,6 +179,9 @@ public class UserControllerTest { USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setUp() throws Exception { runWithDexmakerShareClassLoader(() -> { @@ -789,28 +796,99 @@ public class UserControllerTest { } @Test - public void testStartProfile() throws Exception { - setUpAndStartProfileInBackground(TEST_USER_ID1); + public void testStartManagedProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); startBackgroundUserAssertions(); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @Test - public void testStartProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception { + public void testStartManagedProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception { mockIsUsersOnSecondaryDisplaysEnabled(true); - setUpAndStartProfileInBackground(TEST_USER_ID1); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); startBackgroundUserAssertions(); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @Test - public void testStopProfile() throws Exception { - setUpAndStartProfileInBackground(TEST_USER_ID1); + public void testStopManagedProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); + verifyUserUnassignedFromDisplay(TEST_USER_ID1); + } + + @Test + public void testStopPrivateProfile() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); verifyUserUnassignedFromDisplay(TEST_USER_ID1); + + mSetFlagsRule.disableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* expectLocking= */ true); + verifyUserUnassignedFromDisplay(TEST_USER_ID2); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ false); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking_flagDisabled() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + mSetFlagsRule.disableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + + mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + mSetFlagsRule.enableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking_maxRunningUsersBreached() + throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_MANAGED); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + } + + @Test + public void testStopManagedProfileWithDelayedLocking() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); } /** Tests handleIncomingUser() for a variety of permissions and situations. */ @@ -1001,8 +1079,8 @@ public class UserControllerTest { mUserStates.put(userId, mUserController.getStartedUserState(userId)); } - private void setUpAndStartProfileInBackground(int userId) throws Exception { - setUpUser(userId, UserInfo.FLAG_PROFILE, false, UserManager.USER_TYPE_PROFILE_MANAGED); + private void setUpAndStartProfileInBackground(int userId, String userType) throws Exception { + setUpUser(userId, UserInfo.FLAG_PROFILE, false, userType); assertThat(mUserController.startProfile(userId, /* evenWhenDisabled=*/ false, /* unlockListener= */ null)).isTrue(); @@ -1070,6 +1148,11 @@ public class UserControllerTest { userInfo.preCreated = preCreated; when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated); + + UserTypeDetails userTypeDetails = UserTypeFactory.getUserTypes().get(userType); + assertThat(userTypeDetails).isNotNull(); + when(mInjector.mUserManagerInternalMock.getUserProperties(eq(userId))) + .thenReturn(userTypeDetails.getDefaultUserPropertiesReference()); } private static List<String> getActions(List<Intent> intents) { diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java new file mode 100644 index 000000000000..472a82c02937 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java @@ -0,0 +1,642 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.audio; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.media.audiofx.AudioEffect; +import android.os.Message; +import android.util.Log; + +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@MediumTest +@RunWith(AndroidJUnit4.class) +public class MusicFxHelperTest { + private static final String TAG = "MusicFxHelperTest"; + + @Mock private AudioService mMockAudioService; + @Mock private Context mMockContext; + @Mock private PackageManager mMockPackageManager; + + private ResolveInfo mResolveInfo1 = new ResolveInfo(); + private ResolveInfo mResolveInfo2 = new ResolveInfo(); + private final String mTestPkg1 = "testPkg1", mTestPkg2 = "testPkg2", mTestPkg3 = "testPkg3"; + private final String mMusicFxPkgName = "com.android.musicfx"; + private final int mTestUid1 = 1, mTestUid2 = 2, mTestUid3 = 3, mMusicFxUid = 78; + private final int mTestSession1 = 11, mTestSession2 = 22, mTestSession3 = 33; + + private List<ResolveInfo> mEmptyList = new ArrayList<>(); + private List<ResolveInfo> mSingleList = new ArrayList<>(); + private List<ResolveInfo> mDoubleList = new ArrayList<>(); + + // the class being unit-tested here + @InjectMocks private MusicFxHelper mMusicFxHelper; + + @Before + @SuppressWarnings("DirectInvocationOnMock") + public void setUp() throws Exception { + mMockAudioService = mock(AudioService.class); + mMusicFxHelper = mMockAudioService.getMusicFxHelper(); + MockitoAnnotations.initMocks(this); + + mResolveInfo1.activityInfo = new ActivityInfo(); + mResolveInfo1.activityInfo.packageName = mTestPkg1; + mResolveInfo2.activityInfo = new ActivityInfo(); + mResolveInfo2.activityInfo.packageName = mTestPkg2; + + mSingleList.add(mResolveInfo1); + mDoubleList.add(mResolveInfo1); + mDoubleList.add(mResolveInfo2); + + Assert.assertNotNull(mMusicFxHelper); + } + + private Intent newIntent(String action, String packageName, int sessionId) { + Intent intent = new Intent(action); + if (packageName != null) { + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName); + } + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId); + return intent; + } + + /** + * Helper function to send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION intent with verification. + * + * @throws NameNotFoundException if no such package is available to the caller. + */ + private void openSessionWithResList( + List<ResolveInfo> list, int bind, int broadcast, String packageName, int audioSession, + int uid) { + doReturn(mMockPackageManager).when(mMockContext).getPackageManager(); + doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt()); + if (list != null && list.size() != 0) { + try { + doReturn(uid).when(mMockPackageManager) + .getPackageUidAsUser(eq(packageName), anyObject(), anyInt()); + doReturn(mMusicFxUid).when(mMockPackageManager) + .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt()); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "NameNotFoundException: " + e); + } + } + + Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION, + packageName, audioSession); + mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent); + verify(mMockContext, times(bind)) + .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject()); + verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject()); + } + + /** + * Helper function to send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent with verification. + * + * @throws NameNotFoundException if no such package is available to the caller. + */ + private void closeSessionWithResList( + List<ResolveInfo> list, int unBind, int broadcast, String packageName, + int audioSession, int uid) { + doReturn(mMockPackageManager).when(mMockContext).getPackageManager(); + doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt()); + if (list != null && list.size() != 0) { + try { + doReturn(uid).when(mMockPackageManager) + .getPackageUidAsUser(eq(packageName), anyObject(), anyInt()); + doReturn(mMusicFxUid).when(mMockPackageManager) + .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt()); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "NameNotFoundException: " + e); + } + } + + Intent intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, + packageName, audioSession); + mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent); + verify(mMockContext, times(unBind)).unbindService(anyObject()); + verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject()); + } + + /** + * Helper function to send MSG_EFFECT_CLIENT_GONE message with verification. + */ + private void sendMessage(int msgId, int uid, int unBinds, int broadcasts) { + mMusicFxHelper.handleMessage(Message.obtain(null, msgId, uid /* arg1 */, 0 /* arg2 */)); + verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(anyObject(), anyObject()); + verify(mMockContext, times(unBinds)).unbindService(anyObject()); + } + + /** + * Send invalid message to MusicFxHelper. + */ + @Test + public void testInvalidMessage() { + Log.i(TAG, "running testInvalidMessage"); + + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE - 1, 0, 0, 0); + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE + 1, 0, 0, 0); + } + + /** + * Send client gone message to MusicFxHelper when no client exist. + */ + @Test + public void testGoneMessageWhenNoClient() { + Log.i(TAG, "running testGoneMessageWhenNoClient"); + + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, 0, 0, 0); + } + + /** + * Send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent to MusicFxHelper when no session exist. + */ + @Test + public void testCloseBroadcastIntent() { + Log.i(TAG, "running testCloseBroadcastIntent"); + + closeSessionWithResList(null, 0, 0, null, mTestSession1, mTestUid1); + } + + /** + * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION intent when target application package was set. + * When the target application package was set for an intent, it means this intent is limited + * to a specific target application, as a result MusicFxHelper will not handle this intent. + */ + @Test + public void testBroadcastIntentWithPackage() { + Log.i(TAG, "running testBroadcastIntentWithPackage"); + + Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION, null, 1); + intent.setPackage(mTestPkg1); + mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent); + verify(mMockContext, times(0)) + .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject()); + verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject()); + + intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, null, 1); + intent.setPackage(mTestPkg2); + mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent); + verify(mMockContext, times(0)) + .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject()); + verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject()); + } + + /** + * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with no broadcast receiver. + */ + @Test + public void testBroadcastIntentWithNoPackageAndNoBroadcastReceiver() { + Log.i(TAG, "running testBroadcastIntentWithNoPackageAndNoBroadcastReceiver"); + + openSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1); + closeSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1); + } + + /** + * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with one broadcast receiver. + */ + @Test + public void testBroadcastIntentWithNoPackageAndOneBroadcastReceiver() { + Log.i(TAG, "running testBroadcastIntentWithNoPackageAndOneBroadcastReceiver"); + + int broadcasts = 1, bind = 1, unbind = 1; + openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid1); + + // repeat with different session ID + broadcasts = broadcasts + 1; + bind = bind + 1; + unbind = unbind + 1; + openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession2, mTestUid1); + broadcasts = broadcasts + 1; + closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession2, mTestUid1); + + // repeat with different UID + broadcasts = broadcasts + 1; + bind = bind + 1; + unbind = unbind + 1; + openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid2); + broadcasts = broadcasts + 1; + closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid2); + } + + /** + * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with two broadcast receivers. + */ + @Test + public void testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers() { + Log.i(TAG, "running testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers"); + + openSessionWithResList(mDoubleList, 1, 1, null, mTestSession1, mTestUid1); + closeSessionWithResList(mDoubleList, 1, 2, null, mTestSession1, mTestUid1); + } + + /** + * Open/close session UID not matching. + * No broadcast for mismatching sessionID/UID/packageName. + */ + @Test + public void testBroadcastBadIntents() { + Log.i(TAG, "running testBroadcastBadIntents"); + + int broadcasts = 1; + openSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + // mismatch UID + closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid2); + // mismatch AudioSession + closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + // mismatch packageName + closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg2, mTestSession1, mTestUid1); + + // cleanup with correct UID and session ID + broadcasts = broadcasts + 1; + closeSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + } + + /** + * Open/close sessions with one UID, some with correct intents some with illegal intents. + * No broadcast for mismatching sessionID/UID/packageName. + */ + @Test + public void testBroadcastGoodAndBadIntents() { + Log.i(TAG, "running testBroadcastGoodAndBadIntents"); + + int broadcasts = 1, bind = 1, unbind = 0; + openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + // mismatch packageName, session ID and UID + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2, + mTestUid2); + // mismatch session ID and mismatch UID + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid2); + // mismatch packageName and mismatch UID + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession1, + mTestUid2); + // mismatch packageName and sessionID + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2, + mTestUid1); + // inconsistency package name for same UID + openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid1); + // open session2 with good intent + broadcasts = broadcasts + 1; + openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + + // cleanup with correct UID and session ID + broadcasts = broadcasts + 1; + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + broadcasts = broadcasts + 1; + unbind = unbind + 1; + closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid1); + } + + /** + * Send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION when there is no listener. + */ + @Test + public void testBroadcastOpenSessionWithValidPackageNameAndNoListener() { + Log.i(TAG, "running testBroadcastOpenSessionWithValidPackageNameAndNoListener"); + + // null listener list should not trigger any action + openSessionWithResList(null, 0, 0, mTestPkg1, mTestSession1, mTestUid1); + // empty listener list should not trigger any action + openSessionWithResList(mEmptyList, 0, 0, mTestPkg1, mTestSession1, mTestUid1); + } + + /** + * One MusicFx client, open session and close. + */ + @Test + public void testOpenCloseAudioSession() { + Log.i(TAG, "running testOpenCloseAudioSession"); + + int broadcasts = 1; + openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + } + + /** + * One MusicFx client, open session and close, then gone. + */ + @Test + public void testOpenCloseAudioSessionAndGone() { + Log.i(TAG, "running testOpenCloseAudioSessionAndGone"); + + int broadcasts = 1; + openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + + broadcasts = broadcasts + 1; // 1 open session left + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts); + } + + /** + * One MusicFx client, open session, then UID gone without close. + */ + @Test + public void testOpenOneSessionAndGo() { + Log.i(TAG, "running testOpenOneSessionAndGo"); + + int broadcasts = 1; + openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + + broadcasts = broadcasts + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts); + } + + /** + * Two MusicFx clients open and close sessions. + */ + @Test + public void testOpenTwoSessionsAndClose() { + Log.i(TAG, "running testOpenTwoSessionsAndClose"); + + int broadcasts = 1, bind = 1, unbind = 0; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2, + mTestUid2); + broadcasts = broadcasts + 1; + unbind = unbind + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + + broadcasts = broadcasts + 1; + bind = bind + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + broadcasts = broadcasts + 1; + unbind = unbind + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2, + mTestUid2); + } + + /** + * Two MusicFx clients open sessions, then both UID gone without close. + */ + @Test + public void testOpenTwoSessionsAndGo() { + Log.i(TAG, "running testOpenTwoSessionsAndGo"); + + int broadcasts = 1, bind = 1, unbind = 0; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + + broadcasts = broadcasts + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts); + + broadcasts = broadcasts + 1; + unbind = unbind + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts); + } + + /** + * Two MusicFx clients open sessions, one close but not gone, the other one gone without close. + */ + @Test + public void testTwoSessionsOpenOneCloseOneGo() { + Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo"); + + int broadcasts = 1, bind = 1, unbind = 0; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + + broadcasts = broadcasts + 1; + unbind = unbind + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts); + } + + /** + * One MusicFx client, open multiple audio sessions, and close all sessions. + */ + @Test + public void testTwoSessionsInSameUidOpenClose() { + Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo"); + + int broadcasts = 1, bind = 1, unbind = 0; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + broadcasts = broadcasts + 1; + unbind = unbind + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid1); + } + + /** + * Three MusicFx clients, each with multiple audio sessions, and close all sessions. + */ + @Test + public void testThreeSessionsInThreeUidOpenClose() { + Log.i(TAG, "running testThreeSessionsInThreeUidOpenClose"); + + int broadcasts = 1, bind = 1, unbind = 0; + //client1 + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + // client2 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + // client3 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession3, + mTestUid3); + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2, + mTestUid2); + // all sessions of client1 closed + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid1); + // all sessions of client3 closed + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession1, + mTestUid3); + // all sessions of client2 closed + broadcasts = broadcasts + 1; + // now expect unbind to happen + unbind = unbind + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession3, + mTestUid2); + } + + /** + * Two MusicFx clients, with multiple audio sessions, one close all sessions, and other gone. + */ + @Test + public void testTwoUidOneCloseOneGo() { + Log.i(TAG, "running testTwoUidOneCloseOneGo"); + + int broadcasts = 1, bind = 1, unbind = 0; + //client1 + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + // client2 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession1, mTestUid2); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + // client2 gone + broadcasts = broadcasts + 2; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts); + // client 1 close all sessions + broadcasts = broadcasts + 1; + unbind = unbind + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid1); + } + + /** + * Three MusicFx clients, with multiple audio sessions, all UID gone. + */ + @Test + public void testThreeUidAllGo() { + Log.i(TAG, "running testThreeUidAllGo"); + + int broadcasts = 1, bind = 1, unbind = 0; + //client1 + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + // client2 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2); + // client3 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3); + + // client2 gone + broadcasts = broadcasts + 2; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts); + // client3 gone + broadcasts = broadcasts + 2; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts); + // client 1 gone + broadcasts = broadcasts + 2; + // now expect unbindService to happen + unbind = unbind + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts); + } + + /** + * Three MusicFx clients, multiple audio sessions, open and UID gone in difference sequence. + */ + @Test + public void testThreeUidDiffSequence() { + Log.i(TAG, "running testThreeUidDiffSequence"); + + int broadcasts = 1, bind = 1, unbind = 0; + //client1 + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1); + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1); + // client2 + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2); + // client1 close one session + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1, + mTestUid1); + // client2 open another session + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2); + // client3 open one session + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3); + // client2 gone + broadcasts = broadcasts + 2; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts); + // client3 open another session + broadcasts = broadcasts + 1; + openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3); + // client1 close another session, and gone + broadcasts = broadcasts + 1; + closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2, + mTestUid1); + // last UID client3 gone, unbind + broadcasts = broadcasts + 2; + unbind = unbind + 1; + sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts); + } +} diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java index 33559107dfbb..18e6f0a2cc57 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java @@ -151,6 +151,19 @@ public class GenericWindowPolicyControllerTest { } @Test + public void userNotAllowlisted_launchIsBlocked() { + GenericWindowPolicyController gwpc = createGwpcWithNoAllowedUsers(); + gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); + + ActivityInfo activityInfo = getActivityInfo( + NONBLOCKED_APP_PACKAGE_NAME, + NONBLOCKED_APP_PACKAGE_NAME, + /* displayOnRemoteDevices */ true, + /* targetDisplayCategory */ null); + assertActivityIsBlocked(gwpc, activityInfo); + } + + @Test public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() { GenericWindowPolicyController gwpc = createGwpc(); gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false); @@ -702,6 +715,26 @@ public class GenericWindowPolicyControllerTest { /* customHomeComponent= */ null); } + private GenericWindowPolicyController createGwpcWithNoAllowedUsers() { + return new GenericWindowPolicyController( + 0, + 0, + /* allowedUsers= */ new ArraySet<>(), + /* activityLaunchAllowedByDefault= */ true, + /* activityPolicyExemptions= */ new ArraySet<>(), + /* crossTaskNavigationAllowedByDefault= */ true, + /* crossTaskNavigationExemptions= */ new ArraySet<>(), + /* permissionDialogComponent= */ null, + /* activityListener= */ mActivityListener, + /* pipBlockedCallback= */ mPipBlockedCallback, + /* activityBlockedCallback= */ mActivityBlockedCallback, + /* secureWindowCallback= */ mSecureWindowCallback, + /* intentListenerCallback= */ mIntentListenerCallback, + /* displayCategories= */ new ArraySet<>(), + /* showTasksInHostDeviceRecents= */ true, + /* customHomeComponent= */ null); + } + private GenericWindowPolicyController createGwpcWithCustomHomeComponent( ComponentName homeComponent) { return new GenericWindowPolicyController( diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 9213601a6144..995d1f4d5520 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -444,18 +444,27 @@ public class VirtualDeviceManagerServiceTest { } @Test + public void isDeviceIdValid_invalidDeviceId_returnsFalse() { + assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_INVALID)).isFalse(); + assertThat(mLocalService.isValidVirtualDeviceId(DEVICE_ID_INVALID)).isFalse(); + } + + @Test public void isDeviceIdValid_defaultDeviceId_returnsFalse() { assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse(); + assertThat(mLocalService.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse(); } @Test public void isDeviceIdValid_validVirtualDeviceId_returnsTrue() { assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue(); + assertThat(mLocalService.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue(); } @Test public void isDeviceIdValid_nonExistentDeviceId_returnsFalse() { assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse(); + assertThat(mLocalService.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 5acbe658cf36..a2a8424881d4 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -1722,7 +1722,11 @@ public class HdmiCecLocalDeviceTvTest { mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); + // Skip the retry. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv); } diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java index 37a1a411c703..fb7857494515 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java @@ -1234,7 +1234,7 @@ public class InputMethodUtilsTest { // Init InputMethodSettings for the owner user (userId=0), verify calls can get the // corresponding user's context, contentResolver and the resources configuration. InputMethodUtils.InputMethodSettings settings = new InputMethodUtils.InputMethodSettings( - ownerUserContext, methodMap, 0 /* userId */, true); + methodMap, 0 /* userId */, true); assertEquals(0, settings.getCurrentUserId()); settings.isShowImeWithHardKeyboardEnabled(); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java index d70a4fd555ec..d7ed7c2d6469 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java @@ -69,8 +69,10 @@ public class UserManagerServiceUserPropertiesTest { .setMediaSharedWithParent(false) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setDeleteAppWithParent(false) .setAlwaysVisible(false) + .setCrossProfileContentSharingStrategy(0) .build(); final UserProperties actualProps = new UserProperties(defaultProps); actualProps.setShowInLauncher(14); @@ -84,8 +86,10 @@ public class UserManagerServiceUserPropertiesTest { actualProps.setMediaSharedWithParent(true); actualProps.setCredentialShareableWithParent(false); actualProps.setAuthAlwaysRequiredToDisableQuietMode(true); + actualProps.setAllowStoppingUserWithDelayedLocking(true); actualProps.setDeleteAppWithParent(true); actualProps.setAlwaysVisible(true); + actualProps.setCrossProfileContentSharingStrategy(1); // Write the properties to xml. final ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -128,6 +132,7 @@ public class UserManagerServiceUserPropertiesTest { .setMediaSharedWithParent(true) .setDeleteAppWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setAlwaysVisible(true) .build(); final UserProperties orig = new UserProperties(defaultProps); @@ -137,6 +142,7 @@ public class UserManagerServiceUserPropertiesTest { orig.setInheritDevicePolicy(9456); orig.setDeleteAppWithParent(false); orig.setAuthAlwaysRequiredToDisableQuietMode(true); + orig.setAllowStoppingUserWithDelayedLocking(true); orig.setAlwaysVisible(false); // Test every permission level. (Currently, it's linear so it's easy.) @@ -182,6 +188,8 @@ public class UserManagerServiceUserPropertiesTest { assertEqualGetterOrThrows(orig::getDeleteAppWithParent, copy::getDeleteAppWithParent, exposeAll); assertEqualGetterOrThrows(orig::getAlwaysVisible, copy::getAlwaysVisible, exposeAll); + assertEqualGetterOrThrows(orig::getAllowStoppingUserWithDelayedLocking, + copy::getAllowStoppingUserWithDelayedLocking, exposeAll); // Items requiring hasManagePermission - put them here using hasManagePermission. assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings, @@ -199,6 +207,8 @@ public class UserManagerServiceUserPropertiesTest { copy::isMediaSharedWithParent, true); assertEqualGetterOrThrows(orig::isCredentialShareableWithParent, copy::isCredentialShareableWithParent, true); + assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy, + copy::getCrossProfileContentSharingStrategy, true); } /** @@ -254,7 +264,11 @@ public class UserManagerServiceUserPropertiesTest { .isEqualTo(actual.isCredentialShareableWithParent()); assertThat(expected.isAuthAlwaysRequiredToDisableQuietMode()) .isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode()); + assertThat(expected.getAllowStoppingUserWithDelayedLocking()) + .isEqualTo(actual.getAllowStoppingUserWithDelayedLocking()); assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent()); assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible()); + assertThat(expected.getCrossProfileContentSharingStrategy()) + .isEqualTo(actual.getCrossProfileContentSharingStrategy()); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index 77f693917574..70837061b0bb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -29,7 +29,6 @@ import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; @@ -91,12 +90,14 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(true) .setCredentialShareableWithParent(false) .setAuthAlwaysRequiredToDisableQuietMode(true) + .setAllowStoppingUserWithDelayedLocking(true) .setShowInSettings(900) .setShowInSharingSurfaces(20) .setShowInQuietMode(30) .setInheritDevicePolicy(340) .setDeleteAppWithParent(true) - .setAlwaysVisible(true); + .setAlwaysVisible(true) + .setCrossProfileContentSharingStrategy(1); final UserTypeDetails type = new UserTypeDetails.Builder() .setName("a.name") @@ -167,6 +168,8 @@ public class UserManagerServiceUserTypeTest { assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent()); assertTrue(type.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertTrue(type.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(20, type.getDefaultUserPropertiesReference().getShowInSharingSurfaces()); assertEquals(30, @@ -175,6 +178,8 @@ public class UserManagerServiceUserTypeTest { .getInheritDevicePolicy()); assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent()); assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible()); + assertEquals(1, type.getDefaultUserPropertiesReference() + .getCrossProfileContentSharingStrategy()); assertEquals(23, type.getBadgeLabel(0)); assertEquals(24, type.getBadgeLabel(1)); @@ -231,6 +236,8 @@ public class UserManagerServiceUserTypeTest { assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces()); assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED, props.getShowInQuietMode()); + assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION, + props.getCrossProfileContentSharingStrategy()); assertFalse(type.hasBadge()); } @@ -318,12 +325,14 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(false) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setShowInSettings(20) .setInheritDevicePolicy(21) .setShowInSharingSurfaces(22) .setShowInQuietMode(24) .setDeleteAppWithParent(true) - .setAlwaysVisible(false); + .setAlwaysVisible(false) + .setCrossProfileContentSharingStrategy(1); final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); builders.put(userTypeAosp1, new UserTypeDetails.Builder() @@ -362,6 +371,8 @@ public class UserManagerServiceUserTypeTest { .isCredentialShareableWithParent()); assertFalse(aospType.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertFalse(aospType.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(21, aospType.getDefaultUserPropertiesReference() .getInheritDevicePolicy()); @@ -370,6 +381,8 @@ public class UserManagerServiceUserTypeTest { aospType.getDefaultUserPropertiesReference().getShowInQuietMode()); assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent()); assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible()); + assertEquals(1, aospType.getDefaultUserPropertiesReference() + .getCrossProfileContentSharingStrategy()); // userTypeAosp2 should be modified. aospType = builders.get(userTypeAosp2).createUserTypeDetails(); @@ -413,6 +426,8 @@ public class UserManagerServiceUserTypeTest { .isCredentialShareableWithParent()); assertTrue(aospType.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertTrue(aospType.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(22, aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces()); @@ -422,6 +437,8 @@ public class UserManagerServiceUserTypeTest { .getInheritDevicePolicy()); assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent()); assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible()); + assertEquals(0, aospType.getDefaultUserPropertiesReference() + .getCrossProfileContentSharingStrategy()); // userTypeOem1 should be created. UserTypeDetails.Builder customType = builders.get(userTypeOem1); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 8933c6c56797..a743fff5d2ea 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; import android.annotation.UserIdInt; import android.app.ActivityManager; @@ -219,6 +218,8 @@ public final class UserManagerTest { .isEqualTo(cloneUserProperties.isMediaSharedWithParent()); assertThat(typeProps.isCredentialShareableWithParent()) .isEqualTo(cloneUserProperties.isCredentialShareableWithParent()); + assertThat(typeProps.getCrossProfileContentSharingStrategy()) + .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy()); assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent); assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible); compareDrawables(mUserManager.getUserBadge(), @@ -338,9 +339,15 @@ public final class UserManagerTest { assertThat(typeProps.isAuthAlwaysRequiredToDisableQuietMode()) .isEqualTo(privateProfileUserProperties .isAuthAlwaysRequiredToDisableQuietMode()); + assertThat(typeProps.getCrossProfileContentSharingStrategy()) + .isEqualTo(privateProfileUserProperties.getCrossProfileContentSharingStrategy()); assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent); + assertThrows(SecurityException.class, + privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking); + compareDrawables(mUserManager.getUserBadge(), Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain())); + // Verify private profile parent assertThat(mUserManager.getProfileParent(mainUserId)).isNull(); UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id); diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java index e418d2f8d328..769ec5fac023 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java @@ -57,17 +57,22 @@ import android.content.pm.ProviderInfo; import android.net.Uri; import android.os.Process; import android.os.UserHandle; +import android.platform.test.ravenwood.RavenwoodRule; import android.util.ArraySet; import androidx.test.InstrumentationRegistry; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.util.Arrays; import java.util.Set; public class UriGrantsManagerServiceTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private UriGrantsMockContext mContext; private UriGrantsManagerInternal mService; @@ -79,7 +84,7 @@ public class UriGrantsManagerServiceTest { @Before public void setUp() throws Exception { - mContext = new UriGrantsMockContext(InstrumentationRegistry.getContext()); + mContext = new UriGrantsMockContext(); mService = UriGrantsManagerService.createForTest(mContext.getFilesDir()).getLocalService(); } diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java index 7eb6c9789fe4..4c11de0924ba 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java @@ -21,11 +21,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.annotation.NonNull; import android.app.ActivityManagerInternal; -import android.content.ContentResolver; -import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -33,18 +29,19 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; import android.net.Uri; -import android.os.FileUtils; import android.os.PatternMatcher; import android.os.Process; import android.os.UserHandle; -import android.test.mock.MockContentResolver; +import android.test.mock.MockContext; import android.test.mock.MockPackageManager; import com.android.server.LocalServices; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; -public class UriGrantsMockContext extends ContextWrapper { +public class UriGrantsMockContext extends MockContext { static final String TAG = "UriGrants"; static final int FLAG_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION; @@ -98,19 +95,14 @@ public class UriGrantsMockContext extends ContextWrapper { private final File mDir; private final MockPackageManager mPackage; - private final MockContentResolver mResolver; final ActivityManagerInternal mAmInternal; final PackageManagerInternal mPmInternal; - public UriGrantsMockContext(@NonNull Context base) { - super(base); - mDir = new File(base.getFilesDir(), TAG); - mDir.mkdirs(); - FileUtils.deleteContents(mDir); + public UriGrantsMockContext() throws IOException { + mDir = Files.createTempDirectory(TAG).toFile(); mPackage = new MockPackageManager(); - mResolver = new MockContentResolver(this); mAmInternal = mock(ActivityManagerInternal.class); LocalServices.removeServiceForTest(ActivityManagerInternal.class); @@ -239,11 +231,6 @@ public class UriGrantsMockContext extends ContextWrapper { } @Override - public ContentResolver getContentResolver() { - return mResolver; - } - - @Override public File getFilesDir() { return mDir; } diff --git a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java index 07005a9902d7..4d4f5ed15ad6 100644 --- a/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java +++ b/services/tests/servicestests/src/com/android/server/uri/UriPermissionTest.java @@ -35,12 +35,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.platform.test.ravenwood.RavenwoodRule; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class UriPermissionTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Mock private UriGrantsManagerInternal mService; diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java index 330dbb83e949..861d14a2cf66 100644 --- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java @@ -30,13 +30,19 @@ import androidx.test.filters.SmallTest; import com.android.internal.annotations.GuardedBy; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @SmallTest @Presubmit +@RunWith(Parameterized.class) public class AnrTimerTest { // The commonly used message timeout key. @@ -106,6 +112,16 @@ public class AnrTimerTest { } /** + * Force AnrTimer to use the test parameter for the feature flag. + */ + class TestInjector extends AnrTimer.Injector { + @Override + boolean anrTimerServiceEnabled() { + return mEnabled; + } + } + + /** * An instrumented AnrTimer. */ private static class TestAnrTimer extends AnrTimer<TestArg> { @@ -137,6 +153,17 @@ public class AnrTimerTest { assertEquals(actual.what, MSG_TIMEOUT); } + @Parameters(name = "featureEnabled={0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { {false}, {true} }); + } + + /** True if the feature is enabled. */ + private boolean mEnabled; + + public AnrTimerTest(boolean featureEnabled) { + mEnabled = featureEnabled; + } /** * Verify that a simple expiration succeeds. The timer is started for 10ms. The test diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS index 5e2482825c74..f5b19a1c40ae 100644 --- a/services/tests/servicestests/src/com/android/server/utils/OWNERS +++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS @@ -1,2 +1,5 @@ per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS per-file EventLoggerTest.java = jmtrivi@google.com + +# Bug component : 158088 = per-file AnrTimer*.java +per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java index 5febd02a75a8..8936dc61e6ef 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java @@ -16,13 +16,28 @@ package com.android.server.notification; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import android.app.UiModeManager; import android.app.WallpaperManager; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.display.ColorDisplayManager; import android.os.PowerManager; import android.platform.test.flag.junit.SetFlagsRule; @@ -36,6 +51,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -55,7 +71,7 @@ public class DefaultDeviceEffectsApplierTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = new TestableContext(InstrumentationRegistry.getContext(), null); + mContext = spy(new TestableContext(InstrumentationRegistry.getContext(), null)); mContext.addMockSystemService(PowerManager.class, mPowerManager); mContext.addMockSystemService(ColorDisplayManager.class, mColorDisplayManager); mContext.addMockSystemService(UiModeManager.class, mUiModeManager); @@ -74,25 +90,33 @@ public class DefaultDeviceEffectsApplierTest { .setShouldDisplayGrayscale(true) .setShouldUseNightMode(true) .build(); - mApplier.apply(effects); + mApplier.apply(effects, UPDATE_ORIGIN_USER); verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true)); verify(mColorDisplayManager).setSaturationLevel(eq(0)); verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f)); - verifyZeroInteractions(mUiModeManager); // Coming later; adding now so test fails then. :) + verify(mUiModeManager).setNightModeActivatedForCustomMode( + eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true)); } @Test - public void apply_removesEffects() { + public void apply_removesPreviouslyAppliedEffects() { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder() + .setShouldSuppressAmbientDisplay(true) + .setShouldDimWallpaper(true) + .build(); + mApplier.apply(previousEffects, UPDATE_ORIGIN_USER); + verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true)); + verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f)); + ZenDeviceEffects noEffects = new ZenDeviceEffects.Builder().build(); - mApplier.apply(noEffects); + mApplier.apply(noEffects, UPDATE_ORIGIN_USER); verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false)); - verify(mColorDisplayManager).setSaturationLevel(eq(100)); verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f)); - verifyZeroInteractions(mUiModeManager); + verifyZeroInteractions(mColorDisplayManager, mUiModeManager); } @Test @@ -107,9 +131,104 @@ public class DefaultDeviceEffectsApplierTest { .setShouldDisplayGrayscale(true) .setShouldUseNightMode(true) .build(); - mApplier.apply(effects); + mApplier.apply(effects, UPDATE_ORIGIN_USER); verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true)); // (And no crash from missing services). } + + @Test + public void apply_someEffects_onlyThoseEffectsApplied() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisplayGrayscale(true) + .build(); + mApplier.apply(effects, UPDATE_ORIGIN_USER); + + verify(mColorDisplayManager).setSaturationLevel(eq(0)); + verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f)); + + verify(mPowerManager, never()).suppressAmbientDisplay(anyString(), anyBoolean()); + verify(mUiModeManager, never()).setNightModeActivatedForCustomMode(anyInt(), anyBoolean()); + } + + @Test + public void apply_onlyEffectDeltaApplied() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + mApplier.apply(new ZenDeviceEffects.Builder().setShouldDimWallpaper(true).build(), + UPDATE_ORIGIN_USER); + verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f)); + + // Apply a second effect and remove the first one. + mApplier.apply(new ZenDeviceEffects.Builder().setShouldDisplayGrayscale(true).build(), + UPDATE_ORIGIN_USER); + + // Wallpaper dimming was undone, Grayscale was applied, nothing else was touched. + verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f)); + verify(mColorDisplayManager).setSaturationLevel(eq(0)); + verifyZeroInteractions(mPowerManager); + verifyZeroInteractions(mUiModeManager); + } + + @Test + public void apply_darkThemeFromApp_appliedOnScreenOff() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + ArgumentCaptor<IntentFilter> intentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + + when(mPowerManager.isInteractive()).thenReturn(true); + + mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(), + UPDATE_ORIGIN_APP); + + // Effect was not yet applied, but a broadcast receiver was registered. + verifyZeroInteractions(mUiModeManager); + verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), + intentFilterCaptor.capture(), anyInt()); + assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF); + BroadcastReceiver screenOffReceiver = broadcastReceiverCaptor.getValue(); + + // Now the "screen off" event comes. + screenOffReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + + // So the effect is applied, and we stopped listening for this event. + verify(mUiModeManager).setNightModeActivatedForCustomMode( + eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true)); + verify(mContext).unregisterReceiver(eq(screenOffReceiver)); + } + + @Test + public void apply_darkThemeFromAppWithScreenOff_appliedImmediately() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + when(mPowerManager.isInteractive()).thenReturn(false); + + mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(), + UPDATE_ORIGIN_APP); + + // Effect was applied, and no broadcast receiver was registered. + verify(mUiModeManager).setNightModeActivatedForCustomMode( + eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true)); + verify(mContext, never()).registerReceiver(any(), any(), anyInt()); + } + + @Test + public void testDeviceEffects_darkThemeFromUser_appliedImmediately() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + + when(mPowerManager.isInteractive()).thenReturn(true); + + mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(), + UPDATE_ORIGIN_USER); + + // Effect was applied, and no broadcast receiver was registered. + verify(mUiModeManager).setNightModeActivatedForCustomMode( + eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true)); + verify(mContext, never()).registerReceiver(any(), any(), anyInt()); + } } 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 ee08fd26d631..8bb11a45181e 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -220,6 +220,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; @@ -8929,8 +8930,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.addAutomaticZenRule(rule, "com.android.settings"); // verify that zen mode helper gets passed in a package name of "android" - verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(), - anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), + eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt()); } @Test @@ -8951,8 +8952,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.addAutomaticZenRule(rule, "com.android.settings"); // verify that zen mode helper gets passed in a package name of "android" - verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(), - anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), + eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt()); } @Test @@ -8972,8 +8973,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // verify that zen mode helper gets passed in the package name from the arg, not the owner verify(mockZenModeHelper).addAutomaticZenRule( - eq("another.package"), eq(rule), anyString(), anyInt(), - eq(ZenModeHelper.FROM_APP)); // doesn't count as a system/systemui call + eq("another.package"), eq(rule), eq(ZenModeConfig.UPDATE_ORIGIN_APP), + anyString(), anyInt()); // doesn't count as a system/systemui call } @Test @@ -13185,7 +13186,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0); mBinderService.setNotificationPolicy("package", policy); - verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean()); + verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt()); } @Test @@ -13207,7 +13208,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0); mBinderService.setNotificationPolicy("package", policy); - verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean()); + verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt()); } @Test @@ -13223,7 +13224,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0); mBinderService.setNotificationPolicy("package", policy); - verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean()); + verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt()); } @Test @@ -13271,7 +13272,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY); verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), - eq("package"), anyString(), anyInt(), anyBoolean()); + eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), eq("package"), + anyInt()); } @Test @@ -13280,6 +13282,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); ZenModeHelper zenModeHelper = mock(ZenModeHelper.class); mService.mZenModeHelper = zenModeHelper; + mService.setCallerIsNormalPackage(); when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) .thenReturn(true); when(mCompanionMgr.getAssociations(anyString(), anyInt())) @@ -13292,7 +13295,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY); verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), - eq("package"), anyString(), anyInt(), anyBoolean()); + eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index fe21103096ca..5ddac034d116 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -67,7 +67,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; 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.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -2370,7 +2369,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // create notification channel that can bypass dnd @@ -2380,18 +2379,18 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true, uid, false); assertTrue(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // delete channels mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false); assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2407,7 +2406,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // Recreate a channel & now the app has dnd access granted and can set the bypass dnd field @@ -2417,7 +2416,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { uid, false); assertTrue(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2433,7 +2432,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // create notification channel that can bypass dnd, using local app level settings @@ -2443,18 +2442,18 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true, uid, false); assertTrue(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // delete channels mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false); assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2481,8 +2480,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), - anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2504,7 +2502,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2526,7 +2524,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2542,7 +2540,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false, uid, false); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // update channel so it CAN bypass dnd: @@ -2550,7 +2548,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true); assertTrue(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); // update channel so it can't bypass dnd: @@ -2558,7 +2556,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(false); mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2571,7 +2569,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper.syncChannelsBypassingDnd(); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } @@ -2581,7 +2579,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); assertFalse(mHelper.areChannelsBypassingDnd()); - verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); + verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt()); resetZenModeHelper(); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index ef6fcedf7339..646ee3f6d206 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -45,6 +45,12 @@ import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; import static android.provider.Settings.Global.ZEN_MODE_OFF; import static android.service.notification.Condition.STATE_FALSE; import static android.service.notification.Condition.STATE_TRUE; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_UNKNOWN; +import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; @@ -53,9 +59,6 @@ import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED; import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG; import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW; import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW; -import static com.android.server.notification.ZenModeHelper.FROM_APP; -import static com.android.server.notification.ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI; -import static com.android.server.notification.ZenModeHelper.FROM_USER; import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE; import static com.google.common.truth.Truth.assertThat; @@ -305,8 +308,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL); serializer.endDocument(); serializer.flush(); - mZenModeHelper.setConfig(new ZenModeConfig(), null, "writing xml", Process.SYSTEM_UID, - true); + mZenModeHelper.setConfig(new ZenModeConfig(), null, UPDATE_ORIGIN_INIT, "writing xml", + Process.SYSTEM_UID); return baos; } @@ -321,7 +324,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { serializer.flush(); ZenModeConfig newConfig = new ZenModeConfig(); newConfig.user = userId; - mZenModeHelper.setConfig(newConfig, null, "writing xml", Process.SYSTEM_UID, true); + mZenModeHelper.setConfig(newConfig, null, UPDATE_ORIGIN_INIT, "writing xml", + Process.SYSTEM_UID); return baos; } @@ -838,7 +842,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mConfig = null; // will evaluate config to zen mode off for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked("test", true); + mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -865,7 +869,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked("test", true); + mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -892,7 +896,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked("test", true); + mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -905,8 +909,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { setupZenConfig(); // Turn manual zen mode on - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, - "test", CUSTOM_PKG_UID, false); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, + null, "test", CUSTOM_PKG_UID); // audio manager shouldn't do anything until the handler processes its messages verify(mAudioManager, never()).updateRingerModeAffectedStreamsInternal(); @@ -1185,7 +1189,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { List<StatsEvent> events = new LinkedList<>(); mZenModeHelper.pullRules(events); - mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, "test", CUSTOM_PKG_UID, false); + mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); assertTrue(-1 == mZenModeHelper.mRulesUidCache.getOrDefault(CUSTOM_PKG_NAME + "|" + 0, -1)); } @@ -1240,12 +1245,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { config10.user = 10; config10.allowAlarms = true; config10.allowMedia = true; - mZenModeHelper.setConfig(config10, null, "writeXml", Process.SYSTEM_UID, true); + mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml", + Process.SYSTEM_UID); ZenModeConfig config11 = mZenModeHelper.mConfig.copy(); config11.user = 11; config11.allowAlarms = false; config11.allowMedia = false; - mZenModeHelper.setConfig(config11, null, "writeXml", Process.SYSTEM_UID, true); + mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml", + Process.SYSTEM_UID); // Backup user 10 and reset values. ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10); @@ -1849,8 +1856,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); // We need the package name to be something that's not "android" so there aren't any // existing rules under that package. - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); assertNotNull(id); } try { @@ -1860,8 +1867,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); fail("allowed too many rules to be created"); } catch (IllegalArgumentException e) { // yay @@ -1881,8 +1888,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(si), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); assertNotNull(id); } try { @@ -1892,8 +1899,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); fail("allowed too many rules to be created"); } catch (IllegalArgumentException e) { // yay @@ -1913,8 +1920,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(si), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); assertNotNull(id); } try { @@ -1924,8 +1931,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP, + "test", CUSTOM_PKG_UID); fail("allowed too many rules to be created"); } catch (IllegalArgumentException e) { // yay @@ -1940,8 +1947,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); @@ -1961,8 +1968,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); @@ -1985,11 +1992,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(), new Condition(zenRule.getConditionId(), "", STATE_TRUE), - CUSTOM_PKG_UID, false); + UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); assertEquals(STATE_TRUE, ruleInConfig.condition.state); @@ -2004,8 +2012,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW", null, @@ -2014,7 +2022,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, FROM_APP); + mZenModeHelper.updateAutomaticZenRule(id, zenRule2, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); assertEquals("NEW", ruleInConfig.name); @@ -2029,15 +2037,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); assertTrue(ruleInConfig != null); assertEquals(zenRule.getName(), ruleInConfig.name); - mZenModeHelper.removeAutomaticZenRule(id, "test", CUSTOM_PKG_UID, false); + mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID); assertNull(mZenModeHelper.mConfig.automaticRules.get(id)); } @@ -2049,16 +2057,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test", - CUSTOM_PKG_UID, FROM_APP); + String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); assertTrue(ruleInConfig != null); assertEquals(zenRule.getName(), ruleInConfig.name); - mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), "test", - CUSTOM_PKG_UID, false); + mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), UPDATE_ORIGIN_APP, "test", + CUSTOM_PKG_UID); assertNull(mZenModeHelper.mConfig.automaticRules.get(id)); } @@ -2073,17 +2081,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ComponentName("android", "ScheduleConditionProvider"), sharedUri, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); AutomaticZenRule zenRule2 = new AutomaticZenRule("name2", new ComponentName("android", "ScheduleConditionProvider"), sharedUri, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); Condition condition = new Condition(sharedUri, "", STATE_TRUE); - mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true); + mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) { if (rule.id.equals(id)) { @@ -2097,7 +2106,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } condition = new Condition(sharedUri, "", STATE_FALSE); - mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true); + mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) { if (rule.id.equals(id)) { @@ -2133,7 +2143,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(zde) .build(), - "reasons", 0, FROM_APP); + UPDATE_ORIGIN_APP, "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo( @@ -2167,7 +2177,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(zde) .build(), - "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo(zde); @@ -2195,7 +2205,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(zde) .build(), - "reasons", 0, FROM_USER); + UPDATE_ORIGIN_USER, + "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo(zde); @@ -2212,7 +2223,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0); ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // Good @@ -2223,7 +2234,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(updateFromApp) .build(), - "reasons", 0, FROM_APP); + UPDATE_ORIGIN_APP, "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo( @@ -2244,7 +2255,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0); ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // Good @@ -2254,7 +2265,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new AutomaticZenRule.Builder("Rule", CONDITION_ID) .setDeviceEffects(updateFromSystem) .build(), - "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem); @@ -2271,7 +2282,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0); ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // Good @@ -2281,7 +2292,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new AutomaticZenRule.Builder("Rule", CONDITION_ID) .setDeviceEffects(updateFromUser) .build(), - "reasons", 0, FROM_USER); + UPDATE_ORIGIN_USER, "reasons", 0); AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser); @@ -2293,16 +2304,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { setupZenConfig(); // note that caller=null because that's how it comes in from NMS.setZenMode - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID); // confirm that setting zen mode via setManualZenMode changed the zen mode correctly assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode); assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation); // and also that it works to turn it back off again - mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "", null, Process.SYSTEM_UID); assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode); } @@ -2312,15 +2323,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { setupZenConfig(); // note that caller=null because that's how it comes in from NMS.setZenMode - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID); // confirm that setting zen mode via setManualZenMode changed the zen mode correctly assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode); // and also that it works to turn it back off again - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + null, Process.SYSTEM_UID); assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode); } @@ -2332,13 +2343,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Turn zen mode on (to important_interruptions) // Need to additionally call the looper in order to finish the post-apply-config process - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID); // Now turn zen mode off, but via a different package UID -- this should get registered as // "not an action by the user" because some other app is changing zen mode - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID, - false); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "", null, + CUSTOM_PKG_UID); // In total, this should be 2 loggable changes assertEquals(2, mZenModeEventLogger.numLoggedChanges()); @@ -2397,17 +2408,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, - "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + Process.SYSTEM_UID); // Event 2: "User" turns off the automatic rule (sets it to not enabled) zenRule.setEnabled(false); - mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, - FROM_SYSTEM_OR_SYSTEMUI); + mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); // Add a new system rule AutomaticZenRule systemRule = new AutomaticZenRule("systemRule", @@ -2417,15 +2429,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule, - "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Event 3: turn on the system rule mZenModeHelper.setAutomaticZenRuleState(systemId, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 4: "User" deletes the rule - mZenModeHelper.removeAutomaticZenRule(systemId, "", Process.SYSTEM_UID, true); + mZenModeHelper.removeAutomaticZenRule(systemId, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); // In total, this represents 4 events assertEquals(4, mZenModeEventLogger.numLoggedChanges()); @@ -2486,26 +2499,26 @@ public class ZenModeHelperTest extends UiServiceTestCase { setupZenConfig(); // First just turn zen mode on - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID); // Now change the policy slightly; want to confirm that this'll be reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); newConfig.allowAlarms = true; newConfig.allowRepeatCallers = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID, - true); + mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode // is off. - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + null, Process.SYSTEM_UID); // Change the policy again newConfig.allowMessages = false; newConfig.allowRepeatCallers = true; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID, - true); + mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Total events: we only expect ones for turning on, changing policy, and turning off assertEquals(3, mZenModeEventLogger.numLoggedChanges()); @@ -2548,8 +2561,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Rule 2, same as rule 1 AutomaticZenRule zenRule2 = new AutomaticZenRule("name2", @@ -2558,8 +2571,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Rule 3, has stricter settings than the default settings ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy(); @@ -2572,28 +2585,28 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), ruleConfig.toZenPolicy(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // First: turn on rule 1 mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Second: turn on rule 2 mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Third: turn on rule 3 mZenModeHelper.setAutomaticZenRuleState(id3, new Condition(zenRule3.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Fourth: Turn *off* rule 2 mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_FALSE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // This should result in a total of four events assertEquals(4, mZenModeEventLogger.numLoggedChanges()); @@ -2649,7 +2662,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off // given that we don't have any zen rules active. mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.evaluateZenModeLocked("test", true); + mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); // Check that the change actually took: zen mode should be off now assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode); @@ -2672,8 +2685,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Rule 2, same as rule 1 but owned by the system AutomaticZenRule zenRule2 = new AutomaticZenRule("name2", @@ -2682,37 +2695,37 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Turn on rule 1; call looks like it's from the system. Because setting a condition is // typically an automatic (non-user-initiated) action, expect the calling UID to be // re-evaluated to the one associat.d with CUSTOM_PKG_NAME. mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Second: turn on rule 2. This is a system-owned rule and the UID should not be modified // (nor even looked up; the mock PackageManager won't handle "android" as input). mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Disable rule 1. Because this looks like a user action, the UID should not be modified // from the system-provided one. zenRule.setEnabled(false); - mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, - FROM_SYSTEM_OR_SYSTEMUI); + mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); // Add a manual rule. Any manual rule changes should not get calling uids reassigned. - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - CUSTOM_PKG_UID, false); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, + "", null, CUSTOM_PKG_UID); // Change rule 2's condition, but from some other UID. Since it doesn't look like it's from // the system, we keep the UID info. mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_FALSE), - 12345, false); + UPDATE_ORIGIN_APP, 12345); // That was 5 events total assertEquals(5, mZenModeEventLogger.numLoggedChanges()); @@ -2767,26 +2780,26 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Turn on zen mode with a manual rule with an enabler set. This should *not* count // as a user action, and *should* get its UID reassigned. mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - CUSTOM_PKG_NAME, "", Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID); // Now change apps bypassing to true ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); newConfig.areChannelsBypassingDnd = true; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID, - true); + mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // and then back to false, all without changing anything else newConfig.areChannelsBypassingDnd = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID, - true); + mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Turn off manual mode, call from a package: don't reset UID even though enabler is set - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, - CUSTOM_PKG_NAME, "", 12345, false); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "", + CUSTOM_PKG_NAME, 12345); // And likewise when turning it back on again - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - CUSTOM_PKG_NAME, "", 12345, false); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, + "", CUSTOM_PKG_NAME, 12345); // These are 5 events in total. assertEquals(5, mZenModeEventLogger.numLoggedChanges()); @@ -2831,15 +2844,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { setupZenConfig(); // First just turn zen mode on - mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID); // Now change only the channels part of the policy; want to confirm that this'll be // reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); newConfig.allowPriorityChannels = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID, - true); + mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Total events: one for turning on, one for changing policy assertThat(mZenModeEventLogger.numLoggedChanges()).isEqualTo(2); @@ -2881,13 +2894,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable the rule mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // inspect the consolidated policy. Based on setupZenConfig() values. assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); @@ -2924,13 +2937,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), customPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable the rule; this will update the consolidated policy mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // since this is the only active rule, the consolidated policy should match the custom // policy for every field specified, and take default values for unspecified things @@ -2960,13 +2973,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable rule 1 mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // custom policy for rule 2 ZenPolicy customPolicy = new ZenPolicy.Builder() @@ -2985,12 +2998,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { customPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, - "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable rule 2; this will update the consolidated policy mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // now both rules should be on, and the consolidated policy should reflect the most // restrictive option of each of the two @@ -3022,13 +3035,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), customPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test", - Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable the rule; this will update the consolidated policy mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // confirm that channels make it through assertTrue(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels()); @@ -3045,12 +3058,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { strictPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, - "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // enable rule 2; this will update the consolidated policy mZenModeHelper.setAutomaticZenRuleState(id2, new Condition(zenRule2.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // rule 2 should override rule 1 assertFalse(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels()); @@ -3121,7 +3134,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); - mZenModeHelper.populateZenRule(OWNER.getPackageName(), azr, rule, true, FROM_APP); + mZenModeHelper.populateZenRule(OWNER.getPackageName(), azr, rule, UPDATE_ORIGIN_APP, true); assertEquals(NAME, rule.name); assertEquals(OWNER, rule.component); @@ -3149,7 +3162,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); CountDownLatch latch = new CountDownLatch(1); final int[] actualStatus = new int[1]; @@ -3165,8 +3178,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.addCallback(callback); zenRule.setEnabled(false); - mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, - FROM_SYSTEM_OR_SYSTEMUI); + mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "", Process.SYSTEM_UID); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]); @@ -3184,7 +3197,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, false); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); CountDownLatch latch = new CountDownLatch(1); final int[] actualStatus = new int[1]; @@ -3200,8 +3213,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.addCallback(callback); zenRule.setEnabled(true); - mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, - FROM_SYSTEM_OR_SYSTEMUI); + mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "", Process.SYSTEM_UID); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]); @@ -3220,7 +3233,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); CountDownLatch latch = new CountDownLatch(1); final int[] actualStatus = new int[1]; @@ -3237,7 +3250,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setAutomaticZenRuleState(createdId, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]); @@ -3256,7 +3269,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); CountDownLatch latch = new CountDownLatch(1); final int[] actualStatus = new int[2]; @@ -3274,10 +3287,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setAutomaticZenRuleState(createdId, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); - mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + null, "", Process.SYSTEM_UID); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]); @@ -3296,7 +3309,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); CountDownLatch latch = new CountDownLatch(1); final int[] actualStatus = new int[2]; @@ -3314,11 +3327,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setAutomaticZenRuleState(createdId, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); mZenModeHelper.setAutomaticZenRuleState(createdId, new Condition(zenRule.getConditionId(), "", STATE_FALSE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]); @@ -3336,21 +3349,21 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI); + zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE mZenModeHelper.setAutomaticZenRuleState(createdId, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - Process.SYSTEM_UID, true); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 2: Snooze rule by turning off DND - mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", - Process.SYSTEM_UID, true); + mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "", null, Process.SYSTEM_UID); // Event 3: "User" turns off the automatic rule (sets it to not enabled) zenRule.setEnabled(false); - mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, - FROM_SYSTEM_OR_SYSTEMUI); + mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + "", Process.SYSTEM_UID); assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing); } @@ -3359,18 +3372,20 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testDeviceEffects_applied() { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); + verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); ZenDeviceEffects effects = new ZenDeviceEffects.Builder() .setShouldSuppressAmbientDisplay(true) .setShouldDimWallpaper(true) .build(); String ruleId = addRuleWithEffects(effects); - verify(mDeviceEffectsApplier, never()).apply(any()); + verifyNoMoreInteractions(mDeviceEffectsApplier); - mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); - verify(mDeviceEffectsApplier).apply(eq(effects)); + verify(mDeviceEffectsApplier).apply(eq(effects), eq(UPDATE_ORIGIN_APP)); } @Test @@ -3380,31 +3395,66 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); String ruleId = addRuleWithEffects(zde); - mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); - verify(mDeviceEffectsApplier).apply(eq(zde)); + verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_APP)); - mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_FALSE, CUSTOM_PKG_UID, false); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_FALSE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); - verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS)); + verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_APP)); } @Test + public void testDeviceEffects_changeToConsolidatedEffects_applied() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); + verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); + + String ruleId = addRuleWithEffects( + new ZenDeviceEffects.Builder().setShouldDisplayGrayscale(true).build()); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + mTestableLooper.processAllMessages(); + verify(mDeviceEffectsApplier).apply( + eq(new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(true) + .build()), + eq(UPDATE_ORIGIN_APP)); + + // Now create and activate a second rule that adds more effects. + String secondRuleId = addRuleWithEffects( + new ZenDeviceEffects.Builder().setShouldDimWallpaper(true).build()); + mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + mTestableLooper.processAllMessages(); + + verify(mDeviceEffectsApplier).apply( + eq(new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(true) + .setShouldDimWallpaper(true) + .build()), + eq(UPDATE_ORIGIN_APP)); + } + @Test public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() { mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); + verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); String ruleId = addRuleWithEffects(zde); - mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); - verify(mDeviceEffectsApplier).apply(eq(zde)); + verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_APP)); // Now create and activate a second rule that doesn't add any more effects. String secondRuleId = addRuleWithEffects(zde); - mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, CUSTOM_PKG_UID, - false); + mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); verifyNoMoreInteractions(mDeviceEffectsApplier); @@ -3416,21 +3466,50 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); String ruleId = addRuleWithEffects(zde); - verify(mDeviceEffectsApplier, never()).apply(any()); + verify(mDeviceEffectsApplier, never()).apply(any(), anyInt()); - mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false); + mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); mTestableLooper.processAllMessages(); - verify(mDeviceEffectsApplier, never()).apply(any()); + verify(mDeviceEffectsApplier, never()).apply(any(), anyInt()); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); - verify(mDeviceEffectsApplier).apply(eq(zde)); + verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_INIT)); + } + + @Test + public void testDeviceEffects_onUserSwitch_appliedImmediately() { + mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); + mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); + verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); + + // Initialize default configurations (default rules) for both users. + mZenModeHelper.onUserSwitched(1); + mZenModeHelper.onUserSwitched(2); + + // Current user is now 2. Tweak a rule for user 1 so it's active and has effects. + ZenRule user1Rule = mZenModeHelper.mConfigs.get(1).automaticRules.valueAt(0); + user1Rule.enabled = true; + user1Rule.condition = new Condition(user1Rule.conditionId, "on", STATE_TRUE); + user1Rule.zenDeviceEffects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldUseNightMode(true) + .build(); + verifyNoMoreInteractions(mDeviceEffectsApplier); + + mZenModeHelper.onUserSwitched(1); + mTestableLooper.processAllMessages(); + + verify(mDeviceEffectsApplier).apply(eq(user1Rule.zenDeviceEffects), + eq(UPDATE_ORIGIN_INIT_USER)); } private String addRuleWithEffects(ZenDeviceEffects effects) { AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID) .setDeviceEffects(effects) .build(); - return mZenModeHelper.addAutomaticZenRule("pkg", rule, "", CUSTOM_PKG_UID, FROM_APP); + return mZenModeHelper.addAutomaticZenRule("pkg", rule, UPDATE_ORIGIN_APP, "", + CUSTOM_PKG_UID); } @Test @@ -3455,7 +3534,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS); - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "test", "test", 0); assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -3505,7 +3584,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1); assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse(); - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "test", "test", 0); assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java deleted file mode 100644 index 49efd1bdd92a..000000000000 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.vibrator; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; - -public class VibratorControlServiceTest { - - private VibratorControlService mVibratorControlService; - private final Object mLock = new Object(); - - @Before - public void setUp() throws Exception { - mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock); - } - - @Test - public void testRegisterVibratorController() throws RemoteException { - FakeVibratorController fakeController = new FakeVibratorController(); - mVibratorControlService.registerVibratorController(fakeController); - - assertThat(fakeController.isLinkedToDeath).isTrue(); - } - - @Test - public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest() - throws RemoteException { - FakeVibratorController fakeController = new FakeVibratorController(); - mVibratorControlService.registerVibratorController(fakeController); - mVibratorControlService.unregisterVibratorController(fakeController); - assertThat(fakeController.isLinkedToDeath).isFalse(); - } - - @Test - public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() - throws RemoteException { - FakeVibratorController fakeController1 = new FakeVibratorController(); - FakeVibratorController fakeController2 = new FakeVibratorController(); - mVibratorControlService.registerVibratorController(fakeController1); - - mVibratorControlService.unregisterVibratorController(fakeController2); - assertThat(fakeController1.isLinkedToDeath).isTrue(); - } -} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java deleted file mode 100644 index 79abe21a301d..000000000000 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.vibrator; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.RemoteException; - -import org.junit.Before; -import org.junit.Test; - -public class VibratorControllerHolderTest { - - private final FakeVibratorController mFakeVibratorController = new FakeVibratorController(); - private VibratorControllerHolder mVibratorControllerHolder; - - @Before - public void setUp() throws Exception { - mVibratorControllerHolder = new VibratorControllerHolder(); - } - - @Test - public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException { - mVibratorControllerHolder.setVibratorController(mFakeVibratorController); - assertThat(mVibratorControllerHolder.getVibratorController()) - .isEqualTo(mFakeVibratorController); - assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); - } - - @Test - public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() - throws RemoteException { - mVibratorControllerHolder.setVibratorController(mFakeVibratorController); - mVibratorControllerHolder.setVibratorController(null); - assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); - assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); - } - - @Test - public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() - throws RemoteException { - mVibratorControllerHolder.setVibratorController(mFakeVibratorController); - mVibratorControllerHolder.binderDied(mFakeVibratorController); - assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); - assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); - } - - @Test - public void testBinderDied_withInvalidController_ignoresRequest() - throws RemoteException { - mVibratorControllerHolder.setVibratorController(mFakeVibratorController); - FakeVibratorController imposterVibratorController = new FakeVibratorController(); - mVibratorControllerHolder.binderDied(imposterVibratorController); - assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); - assertThat(mVibratorControllerHolder.getVibratorController()) - .isEqualTo(mFakeVibratorController); - } -} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index a105649c9b5b..4e9bbe0a28fe 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -307,10 +307,9 @@ public class VibratorManagerServiceTest { @Override void addService(String name, IBinder service) { - if (service instanceof VibratorManagerService.ExternalVibratorService) { - mExternalVibratorService = - (VibratorManagerService.ExternalVibratorService) service; - } + Object serviceInstance = service; + mExternalVibratorService = + (VibratorManagerService.ExternalVibratorService) serviceInstance; } HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( @@ -1399,6 +1398,17 @@ public class VibratorManagerServiceTest { } @Test + public void performHapticFeedback_usesServiceAsToken() throws Exception { + VibratorManagerService service = createSystemReadyService(); + + HalVibration vibration = + performHapticFeedbackAndWaitUntilFinished( + service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true); + + assertTrue(vibration.callerToken == service); + } + + @Test public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception { int defaultNotificationIntensity = mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION); diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java deleted file mode 100644 index 7e235870cedc..000000000000 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.vibrator; - -import android.annotation.NonNull; -import android.frameworks.vibrator.IVibratorController; -import android.os.IBinder; -import android.os.RemoteException; - -/** - * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for - * testing. - */ -public final class FakeVibratorController extends IVibratorController.Stub { - - public boolean isLinkedToDeath = false; - - @Override - public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException { - - } - - @Override - public int getInterfaceVersion() throws RemoteException { - return 0; - } - - @Override - public String getInterfaceHash() throws RemoteException { - return null; - } - - @Override - public void linkToDeath(@NonNull DeathRecipient recipient, int flags) { - super.linkToDeath(recipient, flags); - isLinkedToDeath = true; - } - - @Override - public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { - isLinkedToDeath = false; - return super.unlinkToDeath(recipient, flags); - } -} diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index c3074bb0fee8..ef197918deff 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -99,11 +99,6 @@ android:theme="@style/WhiteBackgroundTheme" android:exported="true"/> - <activity android:name="com.android.server.wm.TrustedPresentationCallbackTest$TestActivity" - android:exported="true" - android:showWhenLocked="true" - android:turnScreenOn="true" /> - <activity android:name="android.app.Activity" android:exported="true" android:showWhenLocked="true" diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index ab35da69da7c..9cdec2588501 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -64,6 +64,7 @@ class ShortcutKeyTestBase { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); TestPhoneWindowManager mPhoneWindowManager; + DispatchedKeyHandler mDispatchedKeyHandler = event -> false; final Context mContext = spy(getInstrumentation().getTargetContext()); /** Modifier key to meta state */ @@ -102,6 +103,10 @@ class ShortcutKeyTestBase { mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate); } + protected final void setDispatchedKeyHandler(DispatchedKeyHandler keyHandler) { + mDispatchedKeyHandler = keyHandler; + } + @After public void tearDown() { if (mPhoneWindowManager != null) { @@ -174,9 +179,20 @@ class ShortcutKeyTestBase { int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent); if ((actions & ACTION_PASS_TO_USER) != 0) { if (0 == mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent)) { - mPhoneWindowManager.dispatchUnhandledKey(keyEvent); + if (!mDispatchedKeyHandler.onKeyDispatched(keyEvent)) { + mPhoneWindowManager.dispatchUnhandledKey(keyEvent); + } } } mPhoneWindowManager.dispatchAllPendingEvents(); } + + interface DispatchedKeyHandler { + /** + * Called when a key event is dispatched to app. + * + * @return true if the event is consumed by app. + */ + boolean onKeyDispatched(KeyEvent event); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java index 912e1d3df945..f7ad2a8f5243 100644 --- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java @@ -97,6 +97,35 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { } @Test + public void stemSingleKey_appHasOverridePermission_consumedByApp_notOpenAllApp() { + overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideStartActivity(); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(true); + mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true); + setDispatchedKeyHandler(keyEvent -> true); + + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertNotOpenAllAppView(); + } + + @Test + public void stemSingleKey_appHasOverridePermission_notConsumedByApp_openAllApp() { + overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideStartActivity(); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(true); + mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true); + + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertOpenAllAppView(); + } + + @Test public void stemLongKey_triggerSearchServiceToLaunchAssist() { overrideBehavior( STEM_PRIMARY_BUTTON_LONG_PRESS, @@ -165,6 +194,30 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { mPhoneWindowManager.assertSwitchToRecent(referenceId); } + @Test + public void stemDoubleKey_earlyShortPress_firstPressConsumedByApp_switchToMostRecent() + throws RemoteException { + overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(true); + mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true); + RecentTaskInfo recentTaskInfo = new RecentTaskInfo(); + int referenceId = 666; + recentTaskInfo.persistentId = referenceId; + doReturn(recentTaskInfo).when( + mPhoneWindowManager.mActivityTaskManagerInternal).getMostRecentTaskFromBackground(); + + setDispatchedKeyHandler(keyEvent -> true); + sendKey(KEYCODE_STEM_PRIMARY); + setDispatchedKeyHandler(keyEvent -> false); + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertNotOpenAllAppView(); + mPhoneWindowManager.assertSwitchToRecent(referenceId); + } + private void overrideBehavior(String key, int expectedBehavior) { Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior); } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 43c47458d19f..d057226836a3 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -90,6 +90,7 @@ import android.view.autofill.AutofillManagerInternal; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.util.FrameworkStatsLog; import com.android.server.GestureLauncherService; import com.android.server.LocalServices; @@ -162,6 +163,9 @@ class TestPhoneWindowManager { @Mock private KeyguardServiceDelegate mKeyguardServiceDelegate; + @Mock + private PhoneWindowManager.ButtonOverridePermissionChecker mButtonOverridePermissionChecker; + private StaticMockitoSession mMockitoSession; private OffsettableClock mClock = new OffsettableClock(); private TestLooper mTestLooper = new TestLooper(() -> mClock.now()); @@ -189,6 +193,10 @@ class TestPhoneWindowManager { IActivityManager getActivityManagerService() { return mActivityManagerService; } + + PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { + return mButtonOverridePermissionChecker; + } } TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) { @@ -304,6 +312,11 @@ class TestPhoneWindowManager { doReturn(false).when(mPhoneWindowManager).keyguardOn(); doNothing().when(mContext).startActivityAsUser(any(), any()); doNothing().when(mContext).startActivityAsUser(any(), any(), any()); + + KeyInterceptionInfo interceptionInfo = new KeyInterceptionInfo(0, 0, null, 0); + doReturn(interceptionInfo) + .when(mWindowManagerInternal).getKeyInterceptionInfoFromToken(any()); + Mockito.reset(mContext); } @@ -525,6 +538,11 @@ class TestPhoneWindowManager { mPhoneWindowManager.mPrimaryShortPressTargetActivity = component; } + void overrideFocusedWindowButtonOverridePermission(boolean granted) { + doReturn(granted) + .when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt()); + } + /** * Below functions will check the policy behavior could be invoked. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 4d4d397585b7..ac18f802d1c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -233,6 +233,29 @@ public class BackNavigationControllerTests extends WindowTestsBase { assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); + // reset drawing status, test if top activity is translucent + backNavigationInfo.onBackNavigationFinished(false); + mBackNavigationController.clearBackAnimations(); + makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow()); + // simulate translucent + testCase.recordFront.setOccludesParent(false); + backNavigationInfo = startBackNavigation(); + assertThat(typeToString(backNavigationInfo.getType())) + .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); + testCase.recordFront.setOccludesParent(true); + + // reset drawing status, test if bottom activity is translucent + backNavigationInfo.onBackNavigationFinished(false); + mBackNavigationController.clearBackAnimations(); + makeWindowVisibleAndDrawn(testCase.recordBack.findMainWindow()); + // simulate translucent + testCase.recordBack.setOccludesParent(false); + backNavigationInfo = startBackNavigation(); + assertThat(typeToString(backNavigationInfo.getType())) + .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK)); + testCase.recordBack.setOccludesParent(true); + + // reset drawing status, test canShowWhenLocked backNavigationInfo.onBackNavigationFinished(false); mBackNavigationController.clearBackAnimations(); doReturn(true).when(testCase.recordBack).canShowWhenLocked(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java new file mode 100644 index 000000000000..44b69f18eb04 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.hardware.display.DeviceProductInfo.CONNECTION_TO_SINK_UNKNOWN; +import static android.view.RoundedCorner.POSITION_TOP_LEFT; +import static android.view.RoundedCorners.NO_ROUNDED_CORNERS; + +import static com.android.server.wm.DeferredDisplayUpdater.DEFERRABLE_FIELDS; +import static com.android.server.wm.DeferredDisplayUpdater.DIFF_NOT_WM_DEFERRABLE; +import static com.android.server.wm.DeferredDisplayUpdater.DIFF_WM_DEFERRABLE; +import static com.android.server.wm.DeferredDisplayUpdater.calculateDisplayInfoDiff; +import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.annotation.NonNull; +import android.graphics.Insets; +import android.graphics.Rect; +import android.hardware.display.DeviceProductInfo; +import android.platform.test.annotations.Presubmit; +import android.util.SparseArray; +import android.view.Display; +import android.view.DisplayAddress; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.DisplayShape; +import android.view.RoundedCorner; +import android.view.RoundedCorners; +import android.view.SurfaceControl.RefreshRateRange; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Build/Install/Run: + * atest WmTests:DeferredDisplayUpdaterDiffTest + */ +@SmallTest +@Presubmit +public class DeferredDisplayUpdaterDiffTest { + + private static final Set<String> IGNORED_FIELDS = new HashSet<>(Arrays.asList( + "name" // human-readable name is ignored in equals() checks + )); + + private static final DisplayInfo EMPTY = new DisplayInfo(); + + @Test + public void testCalculateDisplayInfoDiff_allDifferent_returnsChanges() { + final DisplayInfo first = new DisplayInfo(); + final DisplayInfo second = new DisplayInfo(); + makeAllFieldsDifferent(first, second); + + int diff = calculateDisplayInfoDiff(first, second); + + assertWithMessage("Expected to receive a non-zero difference when " + + "there are changes in all fields of DisplayInfo\n" + + "Make sure that you have updated calculateDisplayInfoDiff function after " + + "changing DisplayInfo fields").that(diff).isGreaterThan(0); + } + + @Test + public void testCalculateDisplayInfoDiff_forEveryDifferentField_returnsChanges() { + generateWithSingleDifferentField((first, second, field) -> { + int diff = calculateDisplayInfoDiff(first, second); + + assertWithMessage("Expected to receive a non-zero difference when " + + "there are changes in " + field + "\n" + + "Make sure that you have updated calculateDisplayInfoDiff function after " + + "changing DisplayInfo fields").that(diff).isGreaterThan(0); + }); + } + + @Test + public void testCalculateDisplayInfoDiff_forEveryDifferentField_returnsMatchingChange() { + generateWithSingleDifferentField((first, second, field) -> { + boolean hasDeferrableFieldChange = hasDeferrableFieldChange(first, second); + int expectedDiff = + hasDeferrableFieldChange ? DIFF_WM_DEFERRABLE : DIFF_NOT_WM_DEFERRABLE; + + int diff = calculateDisplayInfoDiff(first, second); + + assertWithMessage("Expected to have diff = " + expectedDiff + + ", for field = " + field + "\n" + + "Make sure that you have updated calculateDisplayInfoDiff function after " + + "changing DisplayInfo fields").that( + diff).isEqualTo(expectedDiff); + }); + } + + /** + * Sets each field of the objects to different values using reflection + */ + private static void makeAllFieldsDifferent(@NonNull DisplayInfo first, + @NonNull DisplayInfo second) { + forEachDisplayInfoField(field -> { + try { + setDifferentFieldValues(first, second, field); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private static boolean hasDeferrableFieldChange(@NonNull DisplayInfo first, + @NonNull DisplayInfo second) { + final DisplayInfo firstDeferrableFieldsOnly = new DisplayInfo(); + final DisplayInfo secondDeferrableFieldsOnly = new DisplayInfo(); + + copyDisplayInfoFields(/* out= */ firstDeferrableFieldsOnly, /* base= */ + EMPTY, /* override= */ first, DEFERRABLE_FIELDS); + copyDisplayInfoFields(/* out= */ secondDeferrableFieldsOnly, /* base= */ + EMPTY, /* override= */ second, DEFERRABLE_FIELDS); + + return !firstDeferrableFieldsOnly.equals(secondDeferrableFieldsOnly); + } + + /** + * Creates pairs of DisplayInfos where only one field is different, the callback is called for + * each field + */ + private static void generateWithSingleDifferentField(DisplayInfoConsumer consumer) { + forEachDisplayInfoField(field -> { + final DisplayInfo first = new DisplayInfo(); + final DisplayInfo second = new DisplayInfo(); + + try { + setDifferentFieldValues(first, second, field); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + + consumer.consume(first, second, field); + }); + } + + private static void setDifferentFieldValues(@NonNull DisplayInfo first, + @NonNull DisplayInfo second, + @NonNull Field field) throws IllegalAccessException { + final Class<?> type = field.getType(); + if (type.equals(int.class)) { + field.setInt(first, 1); + field.setInt(second, 2); + } else if (type.equals(double.class)) { + field.setDouble(first, 1.0); + field.setDouble(second, 2.0); + } else if (type.equals(short.class)) { + field.setShort(first, (short) 1); + field.setShort(second, (short) 2); + } else if (type.equals(long.class)) { + field.setLong(first, 1L); + field.setLong(second, 2L); + } else if (type.equals(char.class)) { + field.setChar(first, 'a'); + field.setChar(second, 'b'); + } else if (type.equals(byte.class)) { + field.setByte(first, (byte) 1); + field.setByte(second, (byte) 2); + } else if (type.equals(float.class)) { + field.setFloat(first, 1.0f); + field.setFloat(second, 2.0f); + } else if (type == boolean.class) { + field.setBoolean(first, true); + field.setBoolean(second, false); + } else if (type.equals(String.class)) { + field.set(first, "one"); + field.set(second, "two"); + } else if (type.equals(DisplayAddress.class)) { + field.set(first, DisplayAddress.fromPhysicalDisplayId(0)); + field.set(second, DisplayAddress.fromPhysicalDisplayId(1)); + } else if (type.equals(DeviceProductInfo.class)) { + field.set(first, new DeviceProductInfo("name", "pnp_id", "product_id1", 2023, + CONNECTION_TO_SINK_UNKNOWN)); + field.set(second, new DeviceProductInfo("name", "pnp_id", "product_id2", 2023, + CONNECTION_TO_SINK_UNKNOWN)); + } else if (type.equals(DisplayCutout.class)) { + field.set(first, + new DisplayCutout(Insets.NONE, new Rect(0, 0, 100, 100), null, null, + null)); + field.set(second, + new DisplayCutout(Insets.NONE, new Rect(0, 0, 200, 200), null, null, + null)); + } else if (type.equals(RoundedCorners.class)) { + field.set(first, NO_ROUNDED_CORNERS); + + final RoundedCorners other = new RoundedCorners(NO_ROUNDED_CORNERS); + other.setRoundedCorner(POSITION_TOP_LEFT, + new RoundedCorner(POSITION_TOP_LEFT, 1, 2, 3)); + field.set(second, other); + } else if (type.equals(DisplayShape.class)) { + field.set(first, DisplayShape.createDefaultDisplayShape(100, 200, false)); + field.set(second, DisplayShape.createDefaultDisplayShape(50, 100, false)); + } else if (type.equals(RefreshRateRange.class)) { + field.set(first, new RefreshRateRange(0, 100)); + field.set(second, new RefreshRateRange(20, 80)); + } else if (type.equals(Display.HdrCapabilities.class)) { + field.set(first, new Display.HdrCapabilities(new int[]{0}, 100, 50, 25)); + field.set(second, new Display.HdrCapabilities(new int[]{1}, 100, 50, 25)); + } else if (type.equals(SparseArray.class) + && ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals( + RefreshRateRange.class)) { + final SparseArray<RefreshRateRange> array1 = new SparseArray<>(); + array1.set(0, new RefreshRateRange(0, 100)); + final SparseArray<RefreshRateRange> array2 = new SparseArray<>(); + array2.set(0, new RefreshRateRange(20, 80)); + field.set(first, array1); + field.set(second, array2); + } else if (type.isArray() && type.getComponentType().equals(int.class)) { + field.set(first, new int[]{0}); + field.set(second, new int[]{1}); + } else if (type.isArray() && type.getComponentType().equals(Display.Mode.class)) { + field.set(first, new Display.Mode[]{new Display.Mode(100, 200, 300)}); + field.set(second, new Display.Mode[]{new Display.Mode(10, 20, 30)}); + } else { + throw new IllegalArgumentException("Field " + field + + " is not supported by this test, please add implementation of setting " + + "different values for this field"); + } + } + + private interface DisplayInfoConsumer { + void consume(DisplayInfo first, DisplayInfo second, Field field); + } + + /** + * Iterates over every non-static field of DisplayInfo class except IGNORED_FIELDS + */ + private static void forEachDisplayInfoField(Consumer<Field> consumer) { + for (Field field : DisplayInfo.class.getDeclaredFields()) { + field.setAccessible(true); + + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + + if (IGNORED_FIELDS.contains(field.getName())) { + continue; + } + + consumer.accept(field); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java new file mode 100644 index 000000000000..dfa595c23e44 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.TransitionController.OnStartCollect; +import com.android.window.flags.Flags; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +/** + * Tests for the {@link DisplayContent} class when FLAG_DEFER_DISPLAY_UPDATES is enabled. + * + * Build/Install/Run: + * atest WmTests:DisplayContentDeferredUpdateTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class DisplayContentDeferredUpdateTests extends WindowTestsBase { + + @Override + protected void onBeforeSystemServicesCreated() { + // Set other flags to their default values + mSetFlagsRule.initAllFlagsToReleaseConfigDefault(); + + mSetFlagsRule.enableFlags(Flags.FLAG_DEFER_DISPLAY_UPDATES); + } + + @Before + public void before() { + mockTransitionsController(/* enabled= */ true); + mockRemoteDisplayChangeController(); + } + + @Test + public void testUpdate_deferrableFieldChangedTransitionStarted_deferrableFieldUpdated() { + performInitialDisplayUpdate(); + + givenDisplayInfo(/* uniqueId= */ "old"); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + + // Emulate that collection has started + captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true); + verify(onUpdated).run(); + clearInvocations(mDisplayContent.mTransitionController, onUpdated); + + givenDisplayInfo(/* uniqueId= */ "new"); + mDisplayContent.requestDisplayUpdate(onUpdated); + captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true); + verify(onUpdated).run(); + assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new"); + } + + @Test + public void testUpdate_nonDeferrableUpdateAndTransitionDeferred_nonDeferrableFieldUpdated() { + performInitialDisplayUpdate(); + + // Update only color mode (non-deferrable field) and keep the same unique id + givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + + verify(onUpdated).run(); + assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123); + } + + @Test + public void testUpdate_nonDeferrableUpdateTwiceAndTransitionDeferred_fieldHasLatestValue() { + performInitialDisplayUpdate(); + + // Update only color mode (non-deferrable field) and keep the same unique id + givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123); + mDisplayContent.requestDisplayUpdate(mock(Runnable.class)); + + assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123); + assertThat(mDisplayContent.getDisplayInfo().uniqueId) + .isEqualTo("initial_unique_id"); + + // Update unique id (deferrable field), keep the same color mode, + // this update should be deferred + givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 123); + mDisplayContent.requestDisplayUpdate(mock(Runnable.class)); + + assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123); + assertThat(mDisplayContent.getDisplayInfo().uniqueId) + .isEqualTo("initial_unique_id"); + + // Update color mode again and keep the same unique id, color mode update + // should not be deferred, unique id update is still deferred as transition + // has not started collecting yet + givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 456); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + + assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(456); + assertThat(mDisplayContent.getDisplayInfo().uniqueId) + .isEqualTo("initial_unique_id"); + + // Mark transition as started collected, so pending changes are applied + captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true); + + // Verify that all fields have the latest values + verify(onUpdated).run(); + assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(456); + assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new_unique_id"); + } + + @Test + public void testUpdate_deferrableFieldUpdatedTransitionPending_fieldNotUpdated() { + performInitialDisplayUpdate(); + givenDisplayInfo(/* uniqueId= */ "old"); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true); + verify(onUpdated).run(); + clearInvocations(mDisplayContent.mTransitionController, onUpdated); + + givenDisplayInfo(/* uniqueId= */ "new"); + mDisplayContent.requestDisplayUpdate(onUpdated); + + captureStartTransitionCollection(); // do not continue by not starting the collection + verify(onUpdated, never()).run(); + assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("old"); + } + + @Test + public void testTwoDisplayUpdates_transitionStarted_displayUpdated() { + performInitialDisplayUpdate(); + givenDisplayInfo(/* uniqueId= */ "old"); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + captureStartTransitionCollection().getValue() + .onCollectStarted(/* deferred= */ true); + verify(onUpdated).run(); + clearInvocations(mDisplayContent.mTransitionController, onUpdated); + + // Perform two display updates while WM is 'busy' + givenDisplayInfo(/* uniqueId= */ "new1"); + Runnable onUpdated1 = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated1); + givenDisplayInfo(/* uniqueId= */ "new2"); + Runnable onUpdated2 = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated2); + + // Continue with the first update + captureStartTransitionCollection().getAllValues().get(0) + .onCollectStarted(/* deferred= */ true); + verify(onUpdated1).run(); + verify(onUpdated2, never()).run(); + assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new1"); + + // Continue with the second update + captureStartTransitionCollection().getAllValues().get(1) + .onCollectStarted(/* deferred= */ true); + verify(onUpdated2).run(); + assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new2"); + } + + private void mockTransitionsController(boolean enabled) { + spyOn(mDisplayContent.mTransitionController); + when(mDisplayContent.mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled); + doReturn(true).when(mDisplayContent.mTransitionController).startCollectOrQueue(any(), + any()); + } + + private void mockRemoteDisplayChangeController() { + spyOn(mDisplayContent.mRemoteDisplayChangeController); + doReturn(true).when(mDisplayContent.mRemoteDisplayChangeController) + .performRemoteDisplayChange(anyInt(), anyInt(), any(), any()); + } + + private ArgumentCaptor<OnStartCollect> captureStartTransitionCollection() { + ArgumentCaptor<OnStartCollect> callbackCaptor = + ArgumentCaptor.forClass(OnStartCollect.class); + verify(mDisplayContent.mTransitionController, atLeast(1)).startCollectOrQueue(any(), + callbackCaptor.capture()); + return callbackCaptor; + } + + private void givenDisplayInfo(String uniqueId) { + givenDisplayInfo(uniqueId, /* colorMode= */ 0); + } + + private void givenDisplayInfo(String uniqueId, int colorMode) { + spyOn(mDisplayContent.mDisplay); + doAnswer(invocation -> { + DisplayInfo info = invocation.getArgument(0); + info.uniqueId = uniqueId; + info.colorMode = colorMode; + return null; + }).when(mDisplayContent.mDisplay).getDisplayInfo(any()); + } + + private void performInitialDisplayUpdate() { + givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 0); + Runnable onUpdated = mock(Runnable.class); + mDisplayContent.requestDisplayUpdate(onUpdated); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java index c0a90b20c999..e77c14a60179 100644 --- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java @@ -19,10 +19,13 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.window.flags.Flags.explicitRefreshRateHints; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -62,9 +65,11 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote(); private static final FrameRateVote FRAME_RATE_VOTE_60_EXACT = - new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_EXACT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); private static final FrameRateVote FRAME_RATE_VOTE_60_PREFERRED = - new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); WindowState createWindow(String name) { WindowState window = createWindow(null, TYPE_APPLICATION, name); @@ -110,6 +115,8 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { any(SurfaceControl.class), anyInt()); verify(appWindow.getPendingTransaction(), never()).setFrameRate( any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); } @Test @@ -140,6 +147,8 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), 1); verify(appWindow.getPendingTransaction(), never()).setFrameRate( any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); } @Test @@ -175,8 +184,17 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), 0); verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 1); - verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( + eq(appWindow.getSurfaceControl()), anyFloat(), + eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS)); + if (explicitRefreshRateHints()) { + verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy( + appWindow.getSurfaceControl(), + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); + } else { + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); + } } @Test @@ -202,8 +220,17 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 2); - verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( + eq(appWindow.getSurfaceControl()), anyFloat(), + eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS)); + if (explicitRefreshRateHints()) { + verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy( + appWindow.getSurfaceControl(), + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); + } else { + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); + } } @Test @@ -229,6 +256,8 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction(), never()).setFrameRate( any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); } @Test @@ -256,6 +285,14 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS); + if (explicitRefreshRateHints()) { + verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy( + appWindow.getSurfaceControl(), + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); + } else { + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); + } } @Test @@ -283,6 +320,8 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction(), never()).setFrameRate( any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); } @Test @@ -310,5 +349,13 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, Surface.CHANGE_FRAME_RATE_ALWAYS); + if (explicitRefreshRateHints()) { + verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy( + appWindow.getSurfaceControl(), + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); + } else { + verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy( + any(SurfaceControl.class), anyInt()); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index ffa1ed926766..38a66a9d5486 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -19,10 +19,13 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.view.InsetsSource.ID_IME; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; @@ -432,6 +435,56 @@ public class InsetsPolicyTest extends WindowTestsBase { } + @SetupWindows(addWindows = W_INPUT_METHOD) + @Test + public void testConsumeImeInsets() { + final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); + final InsetsSource imeSource = new InsetsSource(ID_IME, ime()); + imeSource.setVisible(true); + mImeWindow.mHasSurface = true; + + final WindowState win1 = addWindow(TYPE_APPLICATION, "win1"); + final WindowState win2 = addWindow(TYPE_APPLICATION, "win2"); + + win1.mAboveInsetsState.addSource(imeSource); + win1.mHasSurface = true; + win2.mAboveInsetsState.addSource(imeSource); + win2.mHasSurface = true; + + assertTrue(mImeWindow.isVisible()); + assertTrue(win1.isVisible()); + assertTrue(win2.isVisible()); + + // Make sure both windows have visible IME insets. + assertTrue(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + + win2.mAttrs.privateFlags |= PRIVATE_FLAG_CONSUME_IME_INSETS; + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win2, win2.mAttrs, null, null); + displayPolicy.applyPostLayoutPolicyLw(win1, win1.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + // Make sure win2 doesn't have visible IME insets, but win1 still does. + assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + assertFalse(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + assertTrue(win1.getWindowFrames().hasInsetsChanged()); + + win2.mAttrs.privateFlags &= ~PRIVATE_FLAG_CONSUME_IME_INSETS; + win2.getWindowFrames().setInsetsChanged(false); + + displayPolicy.beginPostLayoutPolicyLw(); + displayPolicy.applyPostLayoutPolicyLw(win2, win2.mAttrs, null, null); + displayPolicy.applyPostLayoutPolicyLw(win1, win1.mAttrs, null, null); + displayPolicy.finishPostLayoutPolicyLw(); + + // Make sure both windows have visible IME insets. + assertTrue(win1.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + assertTrue(win2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); + assertTrue(win1.getWindowFrames().hasInsetsChanged()); + } + private WindowState addNavigationBar() { final Binder owner = new Binder(); final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index 49a888689e60..c9a83b0bc2ee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN; import static android.view.SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -69,15 +70,20 @@ public class RefreshRatePolicyTest extends WindowTestsBase { private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote(); private static final FrameRateVote FRAME_RATE_VOTE_DENY_LIST = - new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); private static final FrameRateVote FRAME_RATE_VOTE_LOW_EXACT = - new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); private static final FrameRateVote FRAME_RATE_VOTE_HI_EXACT = - new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); private static final FrameRateVote FRAME_RATE_VOTE_LOW_PREFERRED = - new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); private static final FrameRateVote FRAME_RATE_VOTE_HI_PREFERRED = - new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); // Parcel and Unparcel the LayoutParams in the window state to test the path the object // travels from the app's process to system server diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java deleted file mode 100644 index c5dd447b5b0c..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; -import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.app.Activity; -import android.platform.test.annotations.Presubmit; -import android.server.wm.CtsWindowInfoUtils; -import android.view.SurfaceControl; -import android.view.SurfaceControl.TrustedPresentationThresholds; - -import androidx.annotation.GuardedBy; -import androidx.test.ext.junit.rules.ActivityScenarioRule; - -import com.android.server.wm.utils.CommonUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.util.function.Consumer; - -/** - * TODO (b/287076178): Move these tests to - * {@link android.view.surfacecontrol.cts.TrustedPresentationCallbackTest} when API is made public - */ -@Presubmit -public class TrustedPresentationCallbackTest { - private static final String TAG = "TrustedPresentationCallbackTest"; - private static final int STABILITY_REQUIREMENT_MS = 500; - private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L; - - private static final float FRACTION_VISIBLE = 0.1f; - - private final Object mResultsLock = new Object(); - @GuardedBy("mResultsLock") - private boolean mResult; - @GuardedBy("mResultsLock") - private boolean mReceivedResults; - - @Rule - public TestName mName = new TestName(); - - @Rule - public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule( - TestActivity.class); - - private TestActivity mActivity; - - @Before - public void setup() { - mActivityRule.getScenario().onActivity(activity -> mActivity = activity); - } - - @After - public void tearDown() { - CommonUtils.waitUntilActivityRemoved(mActivity); - } - - @Test - public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException { - TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( - 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, - Runnable::run, inTrustedPresentationState -> { - synchronized (mResultsLock) { - mResult = inTrustedPresentationState; - mReceivedResults = true; - mResultsLock.notify(); - } - }); - t.apply(); - synchronized (mResultsLock) { - assertResults(); - } - } - - @Test - public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException { - TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( - 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); - Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> { - synchronized (mResultsLock) { - mResult = inTrustedPresentationState; - mReceivedResults = true; - mResultsLock.notify(); - } - }; - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, - Runnable::run, trustedPresentationCallback); - t.apply(); - - synchronized (mResultsLock) { - if (!mReceivedResults) { - mResultsLock.wait(WAIT_TIME_MS); - } - assertResults(); - // reset the state - mReceivedResults = false; - } - - mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t, - trustedPresentationCallback); - t.apply(); - - synchronized (mResultsLock) { - if (!mReceivedResults) { - mResultsLock.wait(WAIT_TIME_MS); - } - // Ensure we waited the full time and never received a notify on the result from the - // callback. - assertFalse("Should never have received a callback", mReceivedResults); - // results shouldn't have changed. - assertTrue(mResult); - } - } - - @GuardedBy("mResultsLock") - private void assertResults() throws InterruptedException { - mResultsLock.wait(WAIT_TIME_MS); - - if (!mReceivedResults) { - CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName()); - } - // Make sure we received the results and not just timed out - assertTrue("Timed out waiting for results", mReceivedResults); - assertTrue(mResult); - } - - public static class TestActivity extends Activity { - } -} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 0e1e0c8e3ad3..3d2340cca378 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -332,6 +332,7 @@ public class UsageStatsService extends SystemService implements mUsageEventListeners.valueAt(i).onUsageEvent(userId, event); } } + return true; } } return false; @@ -1973,6 +1974,8 @@ public class UsageStatsService extends SystemService implements + ": " + Flags.userInteractionTypeApi()); pw.println(" " + Flags.FLAG_USE_PARCELED_LIST + ": " + Flags.useParceledList()); + pw.println(" " + Flags.FLAG_FILTER_BASED_EVENT_QUERY_API + + ": " + Flags.filterBasedEventQueryApi()); final int[] userIds; synchronized (mLock) { @@ -2245,7 +2248,7 @@ public class UsageStatsService extends SystemService implements final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( - callingUid, userId); + callingUid, UserHandle.getCallingUserId()); final long token = Binder.clearCallingIdentity(); try { @@ -2384,6 +2387,7 @@ public class UsageStatsService extends SystemService implements if (!hasQueryPermission(callingPackage)) { return null; } + return queryEventsHelper(UserHandle.getCallingUserId(), query.getBeginTimeMillis(), query.getEndTimeMillis(), callingPackage, query.getEventTypeFilter()); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1e686873c1b0..7cb2cc398c46 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9593,6 +9593,84 @@ public class CarrierConfigManager { "satellite_connection_hysteresis_sec_int"; /** + * This threshold is used when connected to a non-terrestrial LTE network. + * A list of 4 NTN LTE RSRP thresholds above which a signal level is considered POOR, + * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. + * + * Note that the min and max thresholds are fixed at -140 and -44, as explained in + * TS 136.133 9.1.4 - RSRP Measurement Report Mapping. + * <p> + * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside + * these boundaries is considered invalid. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + public static final String KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY = + "ntn_lte_rsrp_thresholds_int_array"; + + /** + * This threshold is used when connected to a non-terrestrial LTE network. + * A list of 4 customized NTN LTE Reference Signal Received Quality (RSRQ) thresholds. + * + * Reference: TS 136.133 v12.6.0 section 9.1.7 - RSRQ Measurement Report Mapping. + * + * 4 threshold integers must be within the boundaries [-34 dB, 3 dB], and the levels are: + * "NONE: [-34, threshold1)" + * "POOR: [threshold1, threshold2)" + * "MODERATE: [threshold2, threshold3)" + * "GOOD: [threshold3, threshold4)" + * "EXCELLENT: [threshold4, 3]" + * + * This key is considered invalid if the format is violated. If the key is invalid or + * not configured, a default value set will apply. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + public static final String KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY = + "ntn_lte_rsrq_thresholds_int_array"; + + /** + * This threshold is used when connected to a non-terrestrial LTE network. + * A list of 4 customized NTN LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds. + * + * 4 threshold integers must be within the boundaries [-20 dB, 30 dB], and the levels are: + * "NONE: [-20, threshold1)" + * "POOR: [threshold1, threshold2)" + * "MODERATE: [threshold2, threshold3)" + * "GOOD: [threshold3, threshold4)" + * "EXCELLENT: [threshold4, 30]" + * + * This key is considered invalid if the format is violated. If the key is invalid or + * not configured, a default value set will apply. + */ + @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + public static final String KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY = + "ntn_lte_rssnr_thresholds_int_array"; + + /** + * This threshold is used when connected to a non-terrestrial LTE network. + * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP), + * Reference Signal Received Quality (RSRQ), or/and Reference Signal Signal to Noise Ratio + * (RSSNR) for the number of NTN LTE signal bars and signal criteria reporting enabling. + * + * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and + * not be used for calculating signal level. If multiple measures are set bit, the parameter + * whose value is smallest is used to indicate the signal level. + * <UL> + * <LI>RSRP = 1 << 0</LI> + * <LI>RSRQ = 1 << 1</LI> + * <LI>RSSNR = 1 << 2</LI> + * </UL> + * <p> The value of this key must be bitwise OR of CellSignalStrengthLte#USE_RSRP, + * CellSignalStrengthLte#USE_RSRQ, CellSignalStrengthLte#USE_RSSNR. + * + * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1). + * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply. + * + */ + @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = + "parameters_used_for_ntn_lte_signal_bar_int"; + + /** * Indicating whether DUN APN should be disabled when the device is roaming. In that case, * the default APN (i.e. internet) will be used for tethering. * @@ -9814,7 +9892,6 @@ public class CarrierConfigManager { * * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT - * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED */ public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG = @@ -10628,6 +10705,32 @@ public class CarrierConfigManager { PersistableBundle.EMPTY); sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false); sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300); + sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY, + // Boundaries: [-140 dBm, -44 dBm] + new int[]{ + -128, /* SIGNAL_STRENGTH_POOR */ + -118, /* SIGNAL_STRENGTH_MODERATE */ + -108, /* SIGNAL_STRENGTH_GOOD */ + -98 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY, + // Boundaries: [-34 dB, 3 dB] + new int[]{ + -20, /* SIGNAL_STRENGTH_POOR */ + -17, /* SIGNAL_STRENGTH_MODERATE */ + -14, /* SIGNAL_STRENGTH_GOOD */ + -11 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putIntArray(KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY, + // Boundaries: [-20 dBm, 30 dBm] + new int[] { + -3, /* SIGNAL_STRENGTH_POOR */ + 1, /* SIGNAL_STRENGTH_MODERATE */ + 5, /* SIGNAL_STRENGTH_GOOD */ + 13 /* SIGNAL_STRENGTH_GREAT */ + }); + sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT, + CellSignalStrengthLte.USE_RSRP); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 5e902613a654..f5282639ae6c 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -263,29 +263,35 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P rssnrThresholds = sRssnrThresholds; rsrpOnly = false; } else { - mParametersUseForLevel = cc.getInt( - CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT); - if (DBG) { - Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel); + if (ss != null && ss.isUsingNonTerrestrialNetwork()) { + if (DBG) log("updateLevel: from NTN_LTE"); + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT); + rsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY); + rsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY); + rssnrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY); + } else { + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT); + rsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY); + rsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY); + rssnrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY); } - rsrpThresholds = cc.getIntArray( - CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY); if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds; + if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds; + if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds; if (DBG) { + Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel); Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds)); - } - rsrqThresholds = cc.getIntArray( - CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY); - if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds; - if (DBG) { Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds)); - } - rssnrThresholds = cc.getIntArray( - CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY); - if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds; - if (DBG) { Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds)); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index e12a815a84f5..b356fde53417 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -109,10 +109,6 @@ import java.util.stream.Collectors; * Then for SDK 35+, if the caller identity is personal profile, then * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa. * - * <p>If the caller needs to see all subscriptions across user profiles, - * use {@link #createForAllUserProfiles} to convert the instance to see all. Additional permission - * may be required as documented on the each API. - * */ @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -1815,9 +1811,6 @@ public class SubscriptionManager { * Then for SDK 35+, if the caller identity is personal profile, then this will return * subscription 1 only and vice versa. * - * <p>If the caller needs to see all subscriptions across user profiles, - * use {@link #createForAllUserProfiles} to convert this instance to see all. - * * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by * {@link SubscriptionInfo#getSubscriptionId}. * @@ -2085,9 +2078,6 @@ public class SubscriptionManager { * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as * it does today. * - * <p>If the caller needs to see all subscriptions across user profiles, - * use {@link #createForAllUserProfiles} to convert this instance to see all. - * * @return The current number of active subscriptions. * * @see #getActiveSubscriptionInfoList() diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java new file mode 100644 index 000000000000..068dfe8f3d11 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.hoststubgen.nativesubstitution; + +import android.os.BadParcelableException; +import android.os.Parcel; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * Native implementation substitutions for the LongArrayMultiStateCounter class. + */ +public class LongArrayMultiStateCounter_host { + + /** + * A reimplementation of {@link com.android.internal.os.LongArrayMultiStateCounter}, only in + * Java instead of native. The majority of the code (in C++) can be found in + * /frameworks/native/libs/battery/MultiStateCounter.h + */ + private static class LongArrayMultiStateCounterRavenwood { + private final int mStateCount; + private final int mArrayLength; + private int mCurrentState; + private long mLastStateChangeTimestampMs = -1; + private long mLastUpdateTimestampMs = -1; + private boolean mEnabled = true; + + private static class State { + private long mTimeInStateSinceUpdate; + private long[] mCounter; + } + + private final State[] mStates; + private final long[] mValues; + private final long[] mDelta; + + LongArrayMultiStateCounterRavenwood(int stateCount, int arrayLength) { + mStateCount = stateCount; + mArrayLength = arrayLength; + mStates = new State[stateCount]; + for (int i = 0; i < mStateCount; i++) { + mStates[i] = new State(); + mStates[i].mCounter = new long[mArrayLength]; + } + mValues = new long[mArrayLength]; + mDelta = new long[mArrayLength]; + } + + public void setEnabled(boolean enabled, long timestampMs) { + if (enabled == mEnabled) { + return; + } + + if (!enabled) { + setState(mCurrentState, timestampMs); + mEnabled = false; + } else { + if (timestampMs < mLastUpdateTimestampMs) { + timestampMs = mLastUpdateTimestampMs; + } + + if (mLastStateChangeTimestampMs >= 0) { + mLastStateChangeTimestampMs = timestampMs; + } + mEnabled = true; + } + } + + public void setState(int state, long timestampMs) { + if (mEnabled && mLastStateChangeTimestampMs >= 0 && mLastUpdateTimestampMs >= 0) { + if (timestampMs < mLastUpdateTimestampMs) { + timestampMs = mLastUpdateTimestampMs; + } + + if (timestampMs >= mLastStateChangeTimestampMs) { + mStates[mCurrentState].mTimeInStateSinceUpdate += + timestampMs - mLastStateChangeTimestampMs; + } else { + for (int i = 0; i < mStateCount; i++) { + mStates[i].mTimeInStateSinceUpdate = 0; + } + } + } + mCurrentState = state; + mLastStateChangeTimestampMs = timestampMs; + } + + public void updateValue(long[] values, long timestampMs) { + if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) { + if (timestampMs < mLastStateChangeTimestampMs) { + timestampMs = mLastStateChangeTimestampMs; + } + + setState(mCurrentState, timestampMs); + + if (mLastUpdateTimestampMs >= 0) { + if (timestampMs > mLastUpdateTimestampMs) { + if (delta(mValues, values, mDelta)) { + long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs; + for (int i = 0; i < mStateCount; i++) { + long timeInState = mStates[i].mTimeInStateSinceUpdate; + if (timeInState > 0) { + add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate); + mStates[i].mTimeInStateSinceUpdate = 0; + } + } + } else { + throw new RuntimeException(); + } + } else if (timestampMs < mLastUpdateTimestampMs) { + throw new RuntimeException(); + } + } + } + System.arraycopy(values, 0, mValues, 0, mArrayLength); + mLastUpdateTimestampMs = timestampMs; + } + + public void incrementValues(long[] delta, long timestampMs) { + long[] values = Arrays.copyOf(mValues, mValues.length); + for (int i = 0; i < mArrayLength; i++) { + values[i] += delta[i]; + } + updateValue(values, timestampMs); + } + + public void getValues(long[] values, int state) { + System.arraycopy(mStates[state].mCounter, 0, values, 0, mArrayLength); + } + + public void reset() { + mLastStateChangeTimestampMs = -1; + mLastUpdateTimestampMs = -1; + for (int i = 0; i < mStateCount; i++) { + mStates[i].mTimeInStateSinceUpdate = 0; + Arrays.fill(mStates[i].mCounter, 0); + } + } + + public void writeToParcel(Parcel parcel) { + parcel.writeInt(mStateCount); + parcel.writeInt(mArrayLength); + for (int i = 0; i < mStateCount; i++) { + parcel.writeLongArray(mStates[i].mCounter); + } + } + + public void initFromParcel(Parcel parcel) { + try { + for (int i = 0; i < mStateCount; i++) { + parcel.readLongArray(mStates[i].mCounter); + } + } catch (Exception e) { + throw new BadParcelableException(e); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int state = 0; state < mStateCount; state++) { + if (state != 0) { + sb.append(", "); + } + sb.append(state).append(": {"); + for (int i = 0; i < mStates[state].mCounter.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(mStates[state].mCounter[i]); + } + sb.append("}"); + } + sb.append("]"); + if (mLastUpdateTimestampMs >= 0) { + sb.append(" updated: ").append(mLastUpdateTimestampMs); + } + if (mLastStateChangeTimestampMs >= 0) { + sb.append(" currentState: ").append(mCurrentState); + if (mLastStateChangeTimestampMs > mLastUpdateTimestampMs) { + sb.append(" stateChanged: ").append(mLastStateChangeTimestampMs); + } + } else { + sb.append(" currentState: none"); + } + return sb.toString(); + } + + private boolean delta(long[] values1, long[] values2, long[] delta) { + if (delta.length != mArrayLength) { + throw new RuntimeException(); + } + + boolean is_delta_valid = true; + for (int i = 0; i < mArrayLength; i++) { + if (values2[i] >= values1[i]) { + delta[i] = values2[i] - values1[i]; + } else { + delta[i] = 0; + is_delta_valid = false; + } + } + + return is_delta_valid; + } + + private void add(long[] counter, long[] delta, long numerator, long denominator) { + if (numerator != denominator) { + for (int i = 0; i < mArrayLength; i++) { + counter[i] += delta[i] * numerator / denominator; + } + } else { + for (int i = 0; i < mArrayLength; i++) { + counter[i] += delta[i]; + } + } + } + } + + public static class LongArrayContainer_host { + private static final HashMap<Long, long[]> sInstances = new HashMap<>(); + private static long sNextId = 1; + + public static long native_init(int arrayLength) { + long[] array = new long[arrayLength]; + long instanceId = sNextId++; + sInstances.put(instanceId, array); + return instanceId; + } + + static long[] getInstance(long instanceId) { + return sInstances.get(instanceId); + } + + public static void native_setValues(long instanceId, long[] values) { + System.arraycopy(values, 0, getInstance(instanceId), 0, values.length); + } + + public static void native_getValues(long instanceId, long[] values) { + System.arraycopy(getInstance(instanceId), 0, values, 0, values.length); + } + + public static boolean native_combineValues(long instanceId, long[] array, int[] indexMap) { + long[] values = getInstance(instanceId); + + boolean nonZero = false; + Arrays.fill(array, 0); + + for (int i = 0; i < values.length; i++) { + int index = indexMap[i]; + if (index < 0 || index >= array.length) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds: [0, " + + (array.length - 1) + "]"); + } + if (values[i] != 0) { + array[index] += values[i]; + nonZero = true; + } + } + return nonZero; + } + } + + private static final HashMap<Long, LongArrayMultiStateCounterRavenwood> sInstances = + new HashMap<>(); + private static long sNextId = 1; + + public static long native_init(int stateCount, int arrayLength) { + LongArrayMultiStateCounterRavenwood instance = new LongArrayMultiStateCounterRavenwood( + stateCount, arrayLength); + long instanceId = sNextId++; + sInstances.put(instanceId, instance); + return instanceId; + } + + private static LongArrayMultiStateCounterRavenwood getInstance(long instanceId) { + return sInstances.get(instanceId); + } + + public static void native_setEnabled(long instanceId, boolean enabled, + long timestampMs) { + getInstance(instanceId).setEnabled(enabled, timestampMs); + } + + public static int native_getStateCount(long instanceId) { + return getInstance(instanceId).mStateCount; + } + + public static int native_getArrayLength(long instanceId) { + return getInstance(instanceId).mArrayLength; + } + + public static void native_updateValues(long instanceId, long containerInstanceId, + long timestampMs) { + getInstance(instanceId).updateValue( + LongArrayContainer_host.getInstance(containerInstanceId), timestampMs); + } + + public static void native_setState(long instanceId, int state, long timestampMs) { + getInstance(instanceId).setState(state, timestampMs); + } + + public static void native_incrementValues(long instanceId, long containerInstanceId, + long timestampMs) { + getInstance(instanceId).incrementValues( + LongArrayContainer_host.getInstance(containerInstanceId), timestampMs); + } + + public static void native_getCounts(long instanceId, long containerInstanceId, int state) { + getInstance(instanceId).getValues(LongArrayContainer_host.getInstance(containerInstanceId), + state); + } + + public static void native_reset(long instanceId) { + getInstance(instanceId).reset(); + } + + public static void native_writeToParcel(long instanceId, Parcel parcel, int flags) { + getInstance(instanceId).writeToParcel(parcel); + } + + public static long native_initFromParcel(Parcel parcel) { + int stateCount = parcel.readInt(); + if (stateCount < 0 || stateCount > 0xEFFF) { + throw new BadParcelableException("stateCount out of range"); + } + // LongArrayMultiStateCounter.cpp uses AParcel, which throws on out-of-data. + if (parcel.dataPosition() >= parcel.dataSize()) { + throw new RuntimeException("Bad parcel"); + } + int arrayLength = parcel.readInt(); + if (parcel.dataPosition() >= parcel.dataSize()) { + throw new RuntimeException("Bad parcel"); + } + long instanceId = native_init(stateCount, arrayLength); + getInstance(instanceId).initFromParcel(parcel); + if (parcel.dataPosition() > parcel.dataSize()) { + throw new RuntimeException("Bad parcel"); + } + return instanceId; + } + + public static String native_toString(long instanceId) { + return getInstance(instanceId).toString(); + } +} diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java new file mode 100644 index 000000000000..cc2fb7bbf236 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.util; + +/** + * Hexadecimal encoding where each byte is represented by two hexadecimal digits. + * @hide + */ +public class HexEncoding { + + private static final char[] LOWER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static final char[] UPPER_CASE_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** Hidden constructor to prevent instantiation. */ + private HexEncoding() {} + + /** + * Encodes the provided byte as a two-digit hexadecimal String value. + * + * @param b byte to encode + * @param upperCase {@code true} to use uppercase letters, {@code false} + * for lowercase + * @return the encoded string + * + * @hide + */ + public static String encodeToString(byte b, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] buf = new char[2]; // We always want two digits. + buf[0] = digits[(b >> 4) & 0xf]; + buf[1] = digits[b & 0xf]; + return new String(buf, 0, 2); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + * + * @param data byte array to encode + * @return the encoded data, using uppercase letters + * + * @hide + */ + public static char[] encode(byte[] data) { + return encode(data, 0, data.length, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + * + * @param data byte array to encode + * @param upperCase {@code true} to use uppercase letters, {@code false} + * for lowercase + * @return the encoded data + * + * @hide + */ + public static char[] encode(byte[] data, boolean upperCase) { + return encode(data, 0, data.length, upperCase); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + * + * @param data byte array containing the data to encode + * @param offset offset of the data to encode in the {@code data} array + * @param len length of the data to encode in the {@code data} array + * @return the encoded data, using uppercase letters + * + * @hide + */ + public static char[] encode(byte[] data, int offset, int len) { + return encode(data, offset, len, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + */ + private static char[] encode(byte[] data, int offset, int len, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS; + char[] result = new char[len * 2]; + for (int i = 0; i < len; i++) { + byte b = data[offset + i]; + int resultIndex = 2 * i; + result[resultIndex] = (digits[(b >> 4) & 0x0f]); + result[resultIndex + 1] = (digits[b & 0x0f]); + } + + return result; + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + * + * @param data byte array to encode + * @return the encoded data, using uppercase letters + * + * @hide + */ + public static String encodeToString(byte[] data) { + return encodeToString(data, true /* upperCase */); + } + + /** + * Encodes the provided data as a sequence of hexadecimal characters. + * + * @param data byte array to encode. + * @param upperCase {@code true} to use uppercase letters, {@code false} + * for lowercase + * @return the encoded data + * + * @hide + */ + public static String encodeToString(byte[] data, boolean upperCase) { + return new String(encode(data, upperCase)); + } + + /** + * Decodes the provided hexadecimal sequence. Odd-length inputs are not + * allowed. + * + * @param encoded string of hexadecimal characters to decode. Letters + * can be either uppercase or lowercase. + * @return the decoded data + * @throws IllegalArgumentException if the input is malformed + * + * @hide + */ + public static byte[] decode(String encoded) throws IllegalArgumentException { + return decode(encoded.toCharArray()); + } + + /** + * Decodes the provided hexadecimal sequence. + * + * @param encoded string of hexadecimal characters to decode. Letters + * can be either uppercase or lowercase. + * @param allowSingleChar If {@code true} odd-length inputs are allowed and + * the first character is interpreted as the lower bits of the first + * result byte. If {@code false} odd-length inputs are not allowed. + * @return the decoded data + * @throws IllegalArgumentException if the input is malformed + * + * @hide + */ + public static byte[] decode(String encoded, boolean allowSingleChar) + throws IllegalArgumentException { + return decode(encoded.toCharArray(), allowSingleChar); + } + + /** + * Decodes the provided hexadecimal sequence. Odd-length inputs are not + * allowed. + * + * @param encoded char array of hexadecimal characters to decode. Letters + * can be either uppercase or lowercase. + * @return the decoded data + * @throws IllegalArgumentException if the input is malformed + * + * @hide + */ + public static byte[] decode(char[] encoded) throws IllegalArgumentException { + return decode(encoded, false); + } + + /** + * Decodes the provided hexadecimal sequence. + * + * @param encoded char array of hexadecimal characters to decode. Letters + * can be either uppercase or lowercase. + * @param allowSingleChar If {@code true} odd-length inputs are allowed and + * the first character is interpreted as the lower bits of the first + * result byte. If {@code false} odd-length inputs are not allowed. + * @return the decoded data + * @throws IllegalArgumentException if the input is malformed + * + * @hide + */ + public static byte[] decode(char[] encoded, boolean allowSingleChar) + throws IllegalArgumentException { + int encodedLength = encoded.length; + int resultLengthBytes = (encodedLength + 1) / 2; + byte[] result = new byte[resultLengthBytes]; + + int resultOffset = 0; + int i = 0; + if (allowSingleChar) { + if ((encodedLength % 2) != 0) { + // Odd number of digits -- the first digit is the lower 4 bits of the first result + // byte. + result[resultOffset++] = (byte) toDigit(encoded, i); + i++; + } + } else { + if ((encodedLength % 2) != 0) { + throw new IllegalArgumentException("Invalid input length: " + encodedLength); + } + } + + for (; i < encodedLength; i += 2) { + result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1)); + } + + return result; + } + + private static int toDigit(char[] str, int offset) throws IllegalArgumentException { + // NOTE: that this isn't really a code point in the traditional sense, since we're + // just rejecting surrogate pairs outright. + int pseudoCodePoint = str[offset]; + + if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') { + return pseudoCodePoint - '0'; + } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') { + return 10 + (pseudoCodePoint - 'a'); + } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') { + return 10 + (pseudoCodePoint - 'A'); + } + + throw new IllegalArgumentException("Illegal char: " + str[offset] + " at offset " + offset); + } +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt index 8354d985abfd..8ca4732f57c4 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt @@ -23,12 +23,16 @@ import com.android.hoststubgen.asm.ClassNodes class AndroidHeuristicsFilter( private val classes: ClassNodes, val aidlPolicy: FilterPolicyWithReason?, + val featureFlagsPolicy: FilterPolicyWithReason?, fallback: OutputFilter ) : DelegatingFilter(fallback) { override fun getPolicyForClass(className: String): FilterPolicyWithReason { if (aidlPolicy != null && classes.isAidlClass(className)) { return aidlPolicy } + if (featureFlagsPolicy != null && classes.isFeatureFlagsClass(className)) { + return featureFlagsPolicy + } return super.getPolicyForClass(className) } } @@ -40,4 +44,16 @@ private fun ClassNodes.isAidlClass(className: String): Boolean { return hasClass(className) && hasClass("$className\$Stub") && hasClass("$className\$Stub\$Proxy") -}
\ No newline at end of file +} + +/** + * @return if a given class "seems like" an feature flags class. + */ +private fun ClassNodes.isFeatureFlagsClass(className: String): Boolean { + // Matches template classes defined here: + // https://cs.android.com/android/platform/superproject/+/master:build/make/tools/aconfig/templates/ + return className.endsWith("/Flags") + || className.endsWith("/FeatureFlags") + || className.endsWith("/FeatureFlagsImpl") + || className.endsWith("/FakeFeatureFlagsImpl"); +} diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index b4354ba84e16..d38a6e34e09f 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -62,7 +62,8 @@ fun createFilterFromTextPolicyFile( var lineNo = 0 - var aidlPolicy: FilterPolicy? = null + var aidlPolicy: FilterPolicyWithReason? = null + var featureFlagsPolicy: FilterPolicyWithReason? = null try { BufferedReader(FileReader(filename)).use { reader -> @@ -130,7 +131,15 @@ fun createFilterFromTextPolicyFile( throw ParseException( "Policy for AIDL classes already defined") } - aidlPolicy = policy + aidlPolicy = policy.withReason("$FILTER_REASON (AIDL)") + } + SpecialClass.FeatureFlags -> { + if (featureFlagsPolicy != null) { + throw ParseException( + "Policy for feature flags already defined") + } + featureFlagsPolicy = + policy.withReason("$FILTER_REASON (feature flags)") } } } @@ -196,10 +205,10 @@ fun createFilterFromTextPolicyFile( } var ret: OutputFilter = imf - aidlPolicy?.let { policy -> + if (aidlPolicy != null || featureFlagsPolicy != null) { log.d("AndroidHeuristicsFilter enabled") ret = AndroidHeuristicsFilter( - classes, policy.withReason("$FILTER_REASON (AIDL)"), imf) + classes, aidlPolicy, featureFlagsPolicy, imf) } return ret } @@ -208,6 +217,7 @@ fun createFilterFromTextPolicyFile( private enum class SpecialClass { NotSpecial, Aidl, + FeatureFlags, } private fun resolveSpecialClass(className: String): SpecialClass { @@ -216,6 +226,7 @@ private fun resolveSpecialClass(className: String): SpecialClass { } when (className.lowercase()) { ":aidl" -> return SpecialClass.Aidl + ":feature_flags" -> return SpecialClass.FeatureFlags } throw ParseException("Invalid special class name \"$className\"") } |