diff options
395 files changed, 10082 insertions, 2379 deletions
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java index 1e2650dbfdc4..3a23b5491678 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java @@ -28,6 +28,7 @@ import android.util.Log; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -50,6 +51,14 @@ public class TypefaceSerializationPerfTest { @Rule public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter(); + @Before + public void setUp() { + // Parse and load the preinstalled fonts in the test process so that: + // (1) Updated fonts do not affect test results. + // (2) Lazy-loading of fonts does not affect test results (esp. testSerializeFontMap). + Typeface.loadPreinstalledSystemFontMap(); + } + @ManualBenchmarkState.ManualBenchmarkTest( warmupDurationNs = WARMUP_DURATION_NS, targetTestDurationNs = TARGET_TEST_DURATION_NS) @@ -61,8 +70,12 @@ public class TypefaceSerializationPerfTest { long elapsedTime = 0; while (state.keepRunning(elapsedTime)) { long startTime = System.nanoTime(); - Typeface.serializeFontMap(systemFontMap); + SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); elapsedTime = System.nanoTime() - startTime; + sharedMemory.close(); + android.util.Log.i(TAG, + "testSerializeFontMap isWarmingUp=" + state.isWarmingUp() + + " elapsedTime=" + elapsedTime); } } diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java index c92c6340a6b4..fb62920681de 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java @@ -153,9 +153,9 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final IWindowSession session = WindowManagerGlobal.getWindowSession(); while (state.keepRunning()) { session.relayout(mWindow, mParams, mWidth, mHeight, - mViewVisibility.getAsInt(), mFlags, mOutFrames, - mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls, - new Bundle()); + mViewVisibility.getAsInt(), mFlags, 0 /* seq */, 0 /* lastSyncSeqId */, + mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, + mOutControls, new Bundle()); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d8e25b6bf5a2..c0a9e675dcb2 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -3078,15 +3078,15 @@ public class AlarmManagerService extends SystemService { pw.decreaseIndent(); pw.println(); } else { - if (mAppStateTracker != null) { - mAppStateTracker.dump(pw); - pw.println(); - } - pw.println("App Standby Parole: " + mAppStandbyParole); pw.println(); } + if (mAppStateTracker != null) { + mAppStateTracker.dump(pw); + pw.println(); + } + final long nowELAPSED = mInjector.getElapsedRealtime(); final long nowUPTIME = SystemClock.uptimeMillis(); final long nowRTC = mInjector.getCurrentTimeMillis(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 73508c8ecd80..794362bd682b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1556,7 +1556,10 @@ public class JobSchedulerService extends com.android.server.SystemService // Create the controllers. mControllers = new ArrayList<StateController>(); - final FlexibilityController flexibilityController = new FlexibilityController(this); + mPrefetchController = new PrefetchController(this); + mControllers.add(mPrefetchController); + final FlexibilityController flexibilityController = + new FlexibilityController(this, mPrefetchController); mControllers.add(flexibilityController); final ConnectivityController connectivityController = new ConnectivityController(this, flexibilityController); @@ -1575,8 +1578,6 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(new ContentObserverController(this)); mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); - mPrefetchController = new PrefetchController(this); - mControllers.add(mPrefetchController); mQuotaController = new QuotaController(this, backgroundJobsController, connectivityController); mControllers.add(mQuotaController); 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 f4ee0aeb8c0c..2e41dfd2888c 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 @@ -23,6 +23,7 @@ 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; @@ -36,6 +37,7 @@ import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; +import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -44,14 +46,13 @@ import com.android.server.job.JobSchedulerService; import com.android.server.utils.AlarmQueue; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Predicate; /** * Controller that tracks the number of flexible constraints being actively satisfied. * Drops constraint for TOP apps and lowers number of required constraints with time. - * - * TODO(b/238887951): handle prefetch */ public final class FlexibilityController extends StateController { private static final String TAG = "JobScheduler.Flexibility"; @@ -68,24 +69,15 @@ public final class FlexibilityController extends StateController { private static final int FLEXIBLE_CONSTRAINTS = JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; - @VisibleForTesting - static final int NUM_JOB_SPECIFIC_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); - @VisibleForTesting static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS); - /** Hard cutoff to remove flexible constraints. */ - private static final long DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS; - - /** - * The default deadline that all flexible constraints should be dropped by if a job lacks - * a deadline. - */ - private static final long DEFAULT_FLEXIBILITY_DEADLINE = 72 * HOUR_IN_MILLIS; + private static final long NO_LIFECYCLE_END = Long.MAX_VALUE; /** * Keeps track of what flexible constraints are satisfied at the moment. @@ -94,30 +86,98 @@ public final class FlexibilityController extends StateController { @VisibleForTesting @GuardedBy("mLock") int mSatisfiedFlexibleConstraints; + + /** Hard cutoff to remove flexible constraints. */ + private long mDeadlineProximityLimitMs = + FcConfig.DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS; + + /** + * The default deadline that all flexible constraints should be dropped by if a job lacks + * a deadline. + */ + private long mFallbackFlexibilityDeadlineMs = + FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS; + @GuardedBy("mLock") - private boolean mFlexibilityEnabled = FcConstants.DEFAULT_FLEXIBILITY_ENABLED; + @VisibleForTesting + boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED; + + private long mMinTimeBetweenFlexibilityAlarmsMs = + FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS; + + /** + * The percent of a job's lifecycle to drop number of required constraints. + * mPercentToDropConstraints[i] denotes that at x% of a Jobs lifecycle, + * the controller should have i+1 constraints dropped. + */ + private int[] mPercentToDropConstraints; @VisibleForTesting @GuardedBy("mLock") final FlexibilityTracker mFlexibilityTracker; - private final FcConstants mFcConstants; + @VisibleForTesting + @GuardedBy("mLock") + final FlexibilityAlarmQueue mFlexibilityAlarmQueue; + @VisibleForTesting + final FcConfig mFcConfig; - private final FlexibilityAlarmQueue mFlexibilityAlarmQueue; - private static final long MIN_TIME_BETWEEN_ALARMS_MS = MINUTE_IN_MILLIS; + @VisibleForTesting + final PrefetchController mPrefetchController; /** - * The percent of a Jobs lifecycle to drop number of required constraints. - * PERCENT_TO_DROP_CONSTRAINTS[i] denotes that at x% of a Jobs lifecycle, - * the controller should have i+1 constraints dropped. + * Stores the beginning of prefetch jobs lifecycle per app as a maximum of + * the last time the app was used and the last time the launch time was updated. */ - private static final int[] PERCENT_TO_DROP_CONSTRAINTS = {50, 60, 70, 80}; + @VisibleForTesting + @GuardedBy("mLock") + final SparseArrayMap<String, Long> mPrefetchLifeCycleStart = new SparseArrayMap<>(); - public FlexibilityController(JobSchedulerService service) { + @VisibleForTesting + final PrefetchController.PrefetchChangedListener mPrefetchChangedListener = + new PrefetchController.PrefetchChangedListener() { + @Override + public void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId, + String pkgName, long prevEstimatedLaunchTime, long newEstimatedLaunchTime) { + synchronized (mLock) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long prefetchThreshold = + mPrefetchController.getLaunchTimeThresholdMs(); + boolean jobWasInPrefetchWindow = prevEstimatedLaunchTime + - prefetchThreshold < nowElapsed; + boolean jobIsInPrefetchWindow = newEstimatedLaunchTime + - prefetchThreshold < nowElapsed; + if (jobIsInPrefetchWindow != jobWasInPrefetchWindow) { + // If the job was in the window previously then changing the start + // of the lifecycle to the current moment without a large change in the + // end would squeeze the window too tight fail to drop constraints. + mPrefetchLifeCycleStart.add(userId, pkgName, Math.max(nowElapsed, + mPrefetchLifeCycleStart.getOrDefault(userId, pkgName, 0L))); + } + for (int i = 0; i < jobs.size(); i++) { + JobStatus js = jobs.valueAt(i); + if (!js.hasFlexibilityConstraint()) { + continue; + } + mFlexibilityTracker.resetJobNumDroppedConstraints(js); + mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js); + } + } + } + }; + + public FlexibilityController( + JobSchedulerService service, PrefetchController prefetchController) { super(service); mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS); - mFcConstants = new FcConstants(); + mFcConfig = new FcConfig(); mFlexibilityAlarmQueue = new FlexibilityAlarmQueue( mContext, JobSchedulerBackgroundThread.get().getLooper()); + mPercentToDropConstraints = + mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; + mPrefetchController = prefetchController; + if (mFlexibilityEnabled) { + mPrefetchController.registerPrefetchChangedListener(mPrefetchChangedListener); + } } /** @@ -131,7 +191,7 @@ public final class FlexibilityController extends StateController { js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY); final long nowElapsed = sElapsedRealtimeClock.millis(); js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js)); - mFlexibilityAlarmQueue.addAlarm(js, getNextConstraintDropTimeElapsed(js)); + mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js); } } @@ -144,6 +204,19 @@ public final class FlexibilityController extends StateController { } } + @Override + @GuardedBy("mLock") + public void onAppRemovedLocked(String packageName, int uid) { + final int userId = UserHandle.getUserId(uid); + mPrefetchLifeCycleStart.delete(userId, packageName); + } + + @Override + @GuardedBy("mLock") + public void onUserRemovedLocked(int userId) { + mPrefetchLifeCycleStart.delete(userId); + } + /** Checks if the flexibility constraint is actively satisfied for a given job. */ @GuardedBy("mLock") boolean isFlexibilitySatisfiedLocked(JobStatus js) { @@ -165,6 +238,7 @@ public final class FlexibilityController extends StateController { * Sets the controller's constraint to a given state. * Changes flexibility constraint satisfaction for affected jobs. */ + @VisibleForTesting void setConstraintSatisfied(int constraint, boolean state) { synchronized (mLock) { final boolean old = (mSatisfiedFlexibleConstraints & constraint) != 0; @@ -187,21 +261,20 @@ public final class FlexibilityController extends StateController { // of satisfied system-wide constraints and iterate to the max number of potentially // satisfied constraints, determined by how many job-specific constraints exist. for (int j = 0; j <= NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS; j++) { - final ArraySet<JobStatus> jobs = mFlexibilityTracker + final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker .getJobsByNumRequiredConstraints(numConstraintsToUpdate + j); - if (jobs == null) { + if (jobsByNumConstraints == null) { // If there are no more jobs to iterate through we can just return. return; } - for (int i = 0; i < jobs.size(); i++) { - JobStatus js = jobs.valueAt(i); + for (int i = 0; i < jobsByNumConstraints.size(); i++) { + JobStatus js = jobsByNumConstraints.valueAt(i); js.setFlexibilityConstraintSatisfied( nowElapsed, isFlexibilitySatisfiedLocked(js)); } } - } } @@ -211,16 +284,85 @@ public final class FlexibilityController extends StateController { return (mSatisfiedFlexibleConstraints & constraint) != 0; } + @VisibleForTesting + @GuardedBy("mLock") + long getLifeCycleBeginningElapsedLocked(JobStatus js) { + if (js.getJob().isPrefetch()) { + final long earliestRuntime = Math.max(js.enqueueTime, js.getEarliestRunTime()); + final long estimatedLaunchTime = + mPrefetchController.getNextEstimatedLaunchTimeLocked(js); + long prefetchWindowStart = mPrefetchLifeCycleStart.getOrDefault( + js.getSourceUserId(), js.getSourcePackageName(), 0L); + if (estimatedLaunchTime != Long.MAX_VALUE) { + prefetchWindowStart = Math.max(prefetchWindowStart, + estimatedLaunchTime - mPrefetchController.getLaunchTimeThresholdMs()); + } + return Math.max(prefetchWindowStart, earliestRuntime); + } + return js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME + ? js.enqueueTime : js.getEarliestRunTime(); + } + + @VisibleForTesting + @GuardedBy("mLock") + long getLifeCycleEndElapsedLocked(JobStatus js, long earliest) { + if (js.getJob().isPrefetch()) { + final long estimatedLaunchTime = + mPrefetchController.getNextEstimatedLaunchTimeLocked(js); + // Prefetch jobs aren't supposed to have deadlines after T. + // But some legacy apps might still schedule them with deadlines. + if (js.getLatestRunTimeElapsed() != JobStatus.NO_LATEST_RUNTIME) { + // If there is a deadline, the earliest time is the end of the lifecycle. + return Math.min( + estimatedLaunchTime - mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS, + js.getLatestRunTimeElapsed()); + } + if (estimatedLaunchTime != Long.MAX_VALUE) { + return estimatedLaunchTime - mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS; + } + // There is no deadline and no estimated launch time. + return NO_LIFECYCLE_END; + } + return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME + ? earliest + mFallbackFlexibilityDeadlineMs : js.getLatestRunTimeElapsed(); + } + + @VisibleForTesting + @GuardedBy("mLock") + int getCurPercentOfLifecycleLocked(JobStatus js) { + final long earliest = getLifeCycleBeginningElapsedLocked(js); + final long latest = getLifeCycleEndElapsedLocked(js, earliest); + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (latest == NO_LIFECYCLE_END || earliest >= nowElapsed) { + return 0; + } + if (nowElapsed > latest || latest == earliest) { + return 100; + } + final int percentInTime = (int) ((nowElapsed - earliest) * 100 / (latest - earliest)); + return percentInTime; + } + /** The elapsed time that marks when the next constraint should be dropped. */ @VisibleForTesting @ElapsedRealtimeLong - long getNextConstraintDropTimeElapsed(JobStatus js) { - final long earliest = js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME - ? js.enqueueTime : js.getEarliestRunTime(); - final long latest = js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME - ? earliest + DEFAULT_FLEXIBILITY_DEADLINE - : js.getLatestRunTimeElapsed(); - final int percent = PERCENT_TO_DROP_CONSTRAINTS[js.getNumDroppedFlexibleConstraints()]; + @GuardedBy("mLock") + long getNextConstraintDropTimeElapsedLocked(JobStatus js) { + final long earliest = getLifeCycleBeginningElapsedLocked(js); + final long latest = getLifeCycleEndElapsedLocked(js, earliest); + return getNextConstraintDropTimeElapsedLocked(js, earliest, latest); + } + + /** The elapsed time that marks when the next constraint should be dropped. */ + @VisibleForTesting + @ElapsedRealtimeLong + @GuardedBy("mLock") + long getNextConstraintDropTimeElapsedLocked(JobStatus js, long earliest, long latest) { + if (latest == NO_LIFECYCLE_END + || js.getNumDroppedFlexibleConstraints() == mPercentToDropConstraints.length) { + return NO_LIFECYCLE_END; + } + final int percent = mPercentToDropConstraints[js.getNumDroppedFlexibleConstraints()]; final long percentInTime = ((latest - earliest) * percent) / 100; return earliest + percentInTime; } @@ -233,10 +375,28 @@ public final class FlexibilityController extends StateController { } final long nowElapsed = sElapsedRealtimeClock.millis(); List<JobStatus> jobsByUid = mService.getJobStore().getJobsByUid(uid); + boolean hasPrefetch = false; for (int i = 0; i < jobsByUid.size(); i++) { JobStatus js = jobsByUid.get(i); if (js.hasFlexibilityConstraint()) { js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js)); + hasPrefetch |= js.getJob().isPrefetch(); + } + } + + // Prefetch jobs can't run when the app is TOP, so it should not be included in their + // lifecycle, and marks the beginning of a new lifecycle. + if (hasPrefetch && prevBias == JobInfo.BIAS_TOP_APP) { + final int userId = UserHandle.getUserId(uid); + final ArraySet<String> pkgs = mService.getPackagesForUidLocked(uid); + if (pkgs == null) { + return; + } + for (int i = 0; i < pkgs.size(); i++) { + String pkg = pkgs.valueAt(i); + mPrefetchLifeCycleStart.add(userId, pkg, + Math.max(mPrefetchLifeCycleStart.getOrDefault(userId, pkg, 0L), + nowElapsed)); } } } @@ -244,8 +404,7 @@ public final class FlexibilityController extends StateController { @Override @GuardedBy("mLock") public void onConstantsUpdatedLocked() { - if (mFcConstants.mShouldReevaluateConstraints) { - // Update job bookkeeping out of band. + if (mFcConfig.mShouldReevaluateConstraints) { JobSchedulerBackgroundThread.getHandler().post(() -> { final ArraySet<JobStatus> changedJobs = new ArraySet<>(); synchronized (mLock) { @@ -255,6 +414,8 @@ public final class FlexibilityController extends StateController { .getJobsByNumRequiredConstraints(j); for (int i = 0; i < jobs.size(); i++) { JobStatus js = jobs.valueAt(i); + mFlexibilityTracker.resetJobNumDroppedConstraints(js); + mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js); if (js.setFlexibilityConstraintSatisfied( nowElapsed, isFlexibilitySatisfiedLocked(js))) { changedJobs.add(js); @@ -272,7 +433,13 @@ public final class FlexibilityController extends StateController { @Override @GuardedBy("mLock") public void prepareForUpdatedConstantsLocked() { - mFcConstants.mShouldReevaluateConstraints = false; + mFcConfig.mShouldReevaluateConstraints = false; + } + + @Override + @GuardedBy("mLock") + public void processConstantLocked(DeviceConfig.Properties properties, String key) { + mFcConfig.processConstantLocked(properties, key); } @VisibleForTesting @@ -281,7 +448,7 @@ public final class FlexibilityController extends StateController { FlexibilityTracker(int numFlexibleConstraints) { mTrackedJobs = new ArrayList<>(); - for (int i = 0; i <= numFlexibleConstraints; i++) { + for (int i = 0; i < numFlexibleConstraints; i++) { mTrackedJobs.add(new ArraySet<JobStatus>()); } } @@ -312,6 +479,19 @@ public final class FlexibilityController extends StateController { mTrackedJobs.get(js.getNumRequiredFlexibleConstraints() - 1).remove(js); } + public void resetJobNumDroppedConstraints(JobStatus js) { + final int curPercent = getCurPercentOfLifecycleLocked(js); + int toDrop = 0; + final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + + (js.getPreferUnmetered() ? 1 : 0); + for (int i = 0; i < jsMaxFlexibleConstraints; i++) { + if (curPercent >= mPercentToDropConstraints[i]) { + toDrop++; + } + } + adjustJobsRequiredConstraints(js, js.getNumDroppedFlexibleConstraints() - toDrop); + } + /** Returns all tracked jobs. */ public ArrayList<ArraySet<JobStatus>> getArrayList() { return mTrackedJobs; @@ -323,6 +503,9 @@ public final class FlexibilityController extends StateController { * Jobs with 0 required flexible constraints are removed from the tracker. */ public boolean adjustJobsRequiredConstraints(JobStatus js, int n) { + if (n == 0) { + return false; + } remove(js); js.adjustNumRequiredFlexibleConstraints(n); final long nowElapsed = sElapsedRealtimeClock.millis(); @@ -342,7 +525,7 @@ public final class FlexibilityController extends StateController { public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { for (int i = 0; i < mTrackedJobs.size(); i++) { ArraySet<JobStatus> jobs = mTrackedJobs.get(i); - for (int j = 0; j < mTrackedJobs.size(); j++) { + for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); if (!predicate.test(js)) { continue; @@ -357,11 +540,12 @@ public final class FlexibilityController extends StateController { } } - private class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> { + @VisibleForTesting + class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> { private FlexibilityAlarmQueue(Context context, Looper looper) { super(context, looper, "*job.flexibility_check*", "Flexible Constraint Check", false, - MIN_TIME_BETWEEN_ALARMS_MS); + mMinTimeBetweenFlexibilityAlarmsMs); } @Override @@ -369,38 +553,100 @@ public final class FlexibilityController extends StateController { return js.getSourceUserId() == userId; } + public void scheduleDropNumConstraintsAlarm(JobStatus js) { + long nextTimeElapsed; + synchronized (mLock) { + final long earliest = getLifeCycleBeginningElapsedLocked(js); + final long latest = getLifeCycleEndElapsedLocked(js, earliest); + nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js, earliest, latest); + if (nextTimeElapsed == NO_LIFECYCLE_END) { + // There is no known or estimated next time to drop a constraint. + removeAlarmForKey(js); + return; + } + + if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) { + mFlexibilityTracker.adjustJobsRequiredConstraints( + js, -js.getNumRequiredFlexibleConstraints()); + return; + } + addAlarm(js, nextTimeElapsed); + } + } + @Override protected void processExpiredAlarms(@NonNull ArraySet<JobStatus> expired) { synchronized (mLock) { - JobStatus js; + ArraySet<JobStatus> changedJobs = new ArraySet<>(); for (int i = 0; i < expired.size(); i++) { - js = expired.valueAt(i); - long time = getNextConstraintDropTimeElapsed(js); - int toDecrease = - js.getLatestRunTimeElapsed() - time < DEADLINE_PROXIMITY_LIMIT_MS - ? -js.getNumRequiredFlexibleConstraints() : -1; - if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, toDecrease)) { - mFlexibilityAlarmQueue.addAlarm(js, time); + JobStatus js = expired.valueAt(i); + boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE); + + final long earliest = getLifeCycleBeginningElapsedLocked(js); + final long latest = getLifeCycleEndElapsedLocked(js, earliest); + final long nowElapsed = sElapsedRealtimeClock.millis(); + + if (latest - nowElapsed < mDeadlineProximityLimitMs) { + mFlexibilityTracker.adjustJobsRequiredConstraints(js, + -js.getNumRequiredFlexibleConstraints()); + } else { + long nextTimeElapsed = + getNextConstraintDropTimeElapsedLocked(js, earliest, latest); + if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1) + && nextTimeElapsed != NO_LIFECYCLE_END) { + mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed); + } + } + if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) { + changedJobs.add(js); } } + mStateChangedListener.onControllerStateChanged(changedJobs); } } } @VisibleForTesting - class FcConstants { + class FcConfig { private boolean mShouldReevaluateConstraints = false; + /** 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"; + static final String KEY_DEADLINE_PROXIMITY_LIMIT = + FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms"; + static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE = + FC_CONFIG_PREFIX + "fallback_flexibility_deadline_ms"; + static final String KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = + FC_CONFIG_PREFIX + "min_alarm_time_flexibility_ms"; + static final String KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS = + FC_CONFIG_PREFIX + "percents_to_drop_num_flexible_constraints"; + private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false; + @VisibleForTesting + static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS; + @VisibleForTesting + static final long DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS = 72 * HOUR_IN_MILLIS; + private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS; + @VisibleForTesting + final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80}; + /** + * If false the controller will not track new jobs + * and the flexibility constraint will always be satisfied. + */ public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED; + /** 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. */ + public long FALLBACK_FLEXIBILITY_DEADLINE_MS = DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS; + public long MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = + DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS; + /** The percentages of a jobs' lifecycle to drop the number of required constraints. */ + public int[] PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS = + DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; - /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ - private static final String FC_CONSTANT_PREFIX = "fc_"; - - static final String KEY_FLEXIBILITY_ENABLED = FC_CONSTANT_PREFIX + "enable_flexibility"; - - // TODO(b/239925946): properly handle DeviceConfig and changing variables @GuardedBy("mLock") public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { @@ -410,9 +656,77 @@ public final class FlexibilityController extends StateController { if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) { mFlexibilityEnabled = FLEXIBILITY_ENABLED; mShouldReevaluateConstraints = true; + if (mFlexibilityEnabled) { + mPrefetchController + .registerPrefetchChangedListener(mPrefetchChangedListener); + } else { + mPrefetchController + .unRegisterPrefetchChangedListener(mPrefetchChangedListener); + } + } + break; + case KEY_DEADLINE_PROXIMITY_LIMIT: + DEADLINE_PROXIMITY_LIMIT_MS = + properties.getLong(key, DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS); + if (mDeadlineProximityLimitMs != DEADLINE_PROXIMITY_LIMIT_MS) { + mDeadlineProximityLimitMs = DEADLINE_PROXIMITY_LIMIT_MS; + mShouldReevaluateConstraints = true; } break; + case KEY_FALLBACK_FLEXIBILITY_DEADLINE: + FALLBACK_FLEXIBILITY_DEADLINE_MS = + properties.getLong(key, DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS); + if (mFallbackFlexibilityDeadlineMs != FALLBACK_FLEXIBILITY_DEADLINE_MS) { + mFallbackFlexibilityDeadlineMs = FALLBACK_FLEXIBILITY_DEADLINE_MS; + mShouldReevaluateConstraints = true; + } + break; + case KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS: + MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = + properties.getLong(key, DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS); + if (mMinTimeBetweenFlexibilityAlarmsMs + != MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS) { + mMinTimeBetweenFlexibilityAlarmsMs = MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS; + mShouldReevaluateConstraints = true; + } + break; + case KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS: + String dropPercentString = properties.getString(key, ""); + PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS = + parsePercentToDropString(dropPercentString); + if (PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS != null + && !Arrays.equals(mPercentToDropConstraints, + PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS)) { + mPercentToDropConstraints = PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS; + mShouldReevaluateConstraints = true; + } + break; + } + } + + private int[] parsePercentToDropString(String s) { + String[] dropPercentString = s.split(","); + int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS]; + if (dropPercentInt.length != dropPercentString.length) { + return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; } + int prevPercent = 0; + for (int i = 0; i < dropPercentString.length; i++) { + try { + dropPercentInt[i] = + Integer.parseInt(dropPercentString[i]); + } catch (NumberFormatException ex) { + Slog.e(TAG, "Provided string was improperly formatted.", ex); + return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; + } + if (dropPercentInt[i] < prevPercent) { + Slog.wtf(TAG, "Percents to drop constraints were not in increasing order."); + return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS; + } + prevPercent = dropPercentInt[i]; + } + + return dropPercentInt; } private void dump(IndentingPrintWriter pw) { @@ -429,8 +743,8 @@ public final class FlexibilityController extends StateController { @VisibleForTesting @NonNull - FcConstants getFcConstants() { - return mFcConstants; + FcConfig getFcConfig() { + return mFcConfig; } @Override @@ -440,6 +754,6 @@ public final class FlexibilityController extends StateController { pw.println(); mFlexibilityTracker.dump(pw, predicate); - mFcConstants.dump(pw); + mFcConfig.dump(pw); } } 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 52882fe5d565..57c731757cc9 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 @@ -51,6 +51,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; @@ -574,7 +575,6 @@ public final class JobStatus { if (!isRequestedExpeditedJob() && satisfiesMinWindowException - && !job.isPrefetch() && lacksSomeFlexibleConstraints) { mNumRequiredFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0); @@ -1669,7 +1669,8 @@ public final class JobStatus { return readinessStatusWithConstraint(constraint, true); } - private boolean readinessStatusWithConstraint(int constraint, boolean value) { + @VisibleForTesting + boolean readinessStatusWithConstraint(int constraint, boolean value) { boolean oldValue = false; int satisfied = mSatisfiedConstraintsOfInterest; switch (constraint) { @@ -1705,6 +1706,15 @@ public final class JobStatus { break; } + // The flexibility constraint relies on other constraints to be satisfied. + // This function lacks the information to determine if flexibility will be satisfied. + // But for the purposes of this function it is still useful to know the jobs' readiness + // not including the flexibility constraint. If flexibility is the constraint in question + // we can proceed as normal. + if (constraint != CONSTRAINT_FLEXIBLE) { + satisfied |= CONSTRAINT_FLEXIBLE; + } + boolean toReturn = isReady(satisfied); switch (constraint) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java index 0f385efae5cc..0945b7e796fd 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java @@ -81,6 +81,8 @@ public class PrefetchController extends StateController { */ @GuardedBy("mLock") private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>(); + @GuardedBy("mLock") + private final ArraySet<PrefetchChangedListener> mPrefetchChangedListeners = new ArraySet<>(); private final ThresholdAlarmListener mThresholdAlarmListener; /** @@ -99,6 +101,13 @@ public class PrefetchController extends StateController { @GuardedBy("mLock") private long mLaunchTimeAllowanceMs = PcConstants.DEFAULT_LAUNCH_TIME_ALLOWANCE_MS; + /** Called by Prefetch Controller after local cache has been updated */ + public interface PrefetchChangedListener { + /** Callback to inform listeners when estimated launch times change. */ + void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId, String pkgName, + long prevEstimatedLaunchTime, long newEstimatedLaunchTime); + } + @SuppressWarnings("FieldCanBeLocal") private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener = new EstimatedLaunchTimeChangedListener() { @@ -291,12 +300,17 @@ public class PrefetchController extends StateController { // Don't bother caching the value unless the app has scheduled prefetch jobs // before. This is based on the assumption that if an app has scheduled a // prefetch job before, then it will probably schedule another one again. + final long prevEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); mEstimatedLaunchTimes.add(userId, pkgName, newEstimatedLaunchTime); if (!jobs.isEmpty()) { final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed); + for (int i = 0; i < mPrefetchChangedListeners.size(); i++) { + mPrefetchChangedListeners.valueAt(i).onPrefetchCacheUpdated(jobs, + userId, pkgName, prevEstimatedLaunchTime, newEstimatedLaunchTime); + } if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) { mStateChangedListener.onControllerStateChanged(jobs); } @@ -448,6 +462,18 @@ public class PrefetchController extends StateController { } } + void registerPrefetchChangedListener(PrefetchChangedListener listener) { + synchronized (mLock) { + mPrefetchChangedListeners.add(listener); + } + } + + void unRegisterPrefetchChangedListener(PrefetchChangedListener listener) { + synchronized (mLock) { + mPrefetchChangedListeners.remove(listener); + } + } + private class PcHandler extends Handler { PcHandler(Looper looper) { super(looper); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java index 8b8a57d248b9..e23860c1eca3 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java @@ -34,8 +34,6 @@ import static com.android.server.tare.TareUtils.getCurrentTimeMillis; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -46,6 +44,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; +import android.util.SparseSetArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; @@ -284,6 +283,7 @@ class Agent { for (int i = 0; i < pkgNames.size(); ++i) { final String pkgName = pkgNames.valueAt(i); + final boolean isVip = mIrs.isVip(userId, pkgName); SparseArrayMap<String, OngoingEvent> ongoingEvents = mCurrentOngoingEvents.get(userId, pkgName); if (ongoingEvents != null) { @@ -298,8 +298,8 @@ class Agent { for (int n = 0; n < size; ++n) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); note.recalculateCosts(economicPolicy, userId, pkgName); - final boolean isAffordable = - isAffordableLocked(newBalance, + final boolean isAffordable = isVip + || isAffordableLocked(newBalance, note.getCachedModifiedPrice(), note.getCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); @@ -313,6 +313,51 @@ class Agent { } @GuardedBy("mLock") + void onVipStatusChangedLocked(final int userId, @NonNull String pkgName) { + final long now = getCurrentTimeMillis(); + final long nowElapsed = SystemClock.elapsedRealtime(); + final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); + + final boolean isVip = mIrs.isVip(userId, pkgName); + SparseArrayMap<String, OngoingEvent> ongoingEvents = + mCurrentOngoingEvents.get(userId, pkgName); + if (ongoingEvents != null) { + mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed); + ongoingEvents.forEach(mOngoingEventUpdater); + } + final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = + mActionAffordabilityNotes.get(userId, pkgName); + if (actionAffordabilityNotes != null) { + final int size = actionAffordabilityNotes.size(); + final long newBalance = + mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance(); + for (int n = 0; n < size; ++n) { + final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); + note.recalculateCosts(economicPolicy, userId, pkgName); + final boolean isAffordable = isVip + || isAffordableLocked(newBalance, + note.getCachedModifiedPrice(), note.getCtp()); + if (note.isCurrentlyAffordable() != isAffordable) { + note.setNewAffordability(isAffordable); + mIrs.postAffordabilityChanged(userId, pkgName, note); + } + } + } + scheduleBalanceCheckLocked(userId, pkgName); + } + + @GuardedBy("mLock") + void onVipStatusChangedLocked(@NonNull SparseSetArray<String> pkgs) { + for (int u = pkgs.size() - 1; u >= 0; --u) { + final int userId = pkgs.keyAt(u); + + for (int p = pkgs.sizeAt(u) - 1; p >= 0; --p) { + onVipStatusChangedLocked(userId, pkgs.valueAt(u, p)); + } + } + } + + @GuardedBy("mLock") private void onAnythingChangedLocked(final boolean updateOngoingEvents) { final long now = getCurrentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); @@ -349,11 +394,12 @@ class Agent { if (actionAffordabilityNotes != null) { final int size = actionAffordabilityNotes.size(); final long newBalance = getBalanceLocked(userId, pkgName); + final boolean isVip = mIrs.isVip(userId, pkgName); for (int n = 0; n < size; ++n) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); note.recalculateCosts(economicPolicy, userId, pkgName); - final boolean isAffordable = - isAffordableLocked(newBalance, + final boolean isAffordable = isVip + || isAffordableLocked(newBalance, note.getCachedModifiedPrice(), note.getCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); @@ -454,6 +500,14 @@ class Agent { "Tried to adjust system balance for " + appToString(userId, pkgName)); return; } + if (mIrs.isVip(userId, pkgName)) { + // This could happen if the app was made a VIP after it started performing actions. + // Continue recording the transaction for debugging purposes, but don't let it change + // any numbers. + transaction = new Ledger.Transaction( + transaction.startTimeMs, transaction.endTimeMs, + transaction.eventId, transaction.tag, 0 /* delta */, transaction.ctp); + } final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); final long originalBalance = ledger.getCurrentBalance(); if (transaction.delta > 0 @@ -479,10 +533,11 @@ class Agent { mActionAffordabilityNotes.get(userId, pkgName); if (actionAffordabilityNotes != null) { final long newBalance = ledger.getCurrentBalance(); + final boolean isVip = mIrs.isVip(userId, pkgName); for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); - final boolean isAffordable = - isAffordableLocked(newBalance, + final boolean isAffordable = isVip + || isAffordableLocked(newBalance, note.getCachedModifiedPrice(), note.getCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); @@ -606,13 +661,12 @@ class Agent { } /** Returns true if an app should be given credits in the general distributions. */ - private boolean shouldGiveCredits(@NonNull PackageInfo packageInfo) { - final ApplicationInfo applicationInfo = packageInfo.applicationInfo; + private boolean shouldGiveCredits(@NonNull InstalledPackageInfo packageInfo) { // Skip apps that wouldn't be doing any work. Giving them ARCs would be wasteful. - if (applicationInfo == null || !applicationInfo.hasCode()) { + if (!packageInfo.hasCode) { return false; } - final int userId = UserHandle.getUserId(packageInfo.applicationInfo.uid); + final int userId = UserHandle.getUserId(packageInfo.uid); // No point allocating ARCs to the system. It can do whatever it wants. return !mIrs.isSystem(userId, packageInfo.packageName); } @@ -623,15 +677,15 @@ class Agent { @GuardedBy("mLock") void distributeBasicIncomeLocked(int batteryLevel) { - List<PackageInfo> pkgs = mIrs.getInstalledPackages(); + final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages(); final long now = getCurrentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { - final PackageInfo pkgInfo = pkgs.get(i); + final InstalledPackageInfo pkgInfo = pkgs.get(i); if (!shouldGiveCredits(pkgInfo)) { continue; } - final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid); + final int userId = UserHandle.getUserId(pkgInfo.uid); final String pkgName = pkgInfo.packageName; final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName); @@ -659,11 +713,11 @@ class Agent { @GuardedBy("mLock") void grantBirthrightsLocked(final int userId) { - final List<PackageInfo> pkgs = mIrs.getInstalledPackages(userId); + final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages(userId); final long now = getCurrentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { - final PackageInfo packageInfo = pkgs.get(i); + final InstalledPackageInfo packageInfo = pkgs.get(i); if (!shouldGiveCredits(packageInfo)) { continue; } @@ -869,7 +923,7 @@ class Agent { private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) { SparseArrayMap<String, OngoingEvent> ongoingEvents = mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents == null) { + if (ongoingEvents == null || mIrs.isVip(userId, pkgName)) { // No ongoing transactions. No reason to schedule mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName)); return; @@ -1062,9 +1116,10 @@ class Agent { note.setNewAffordability(true); return; } + final boolean isVip = mIrs.isVip(userId, pkgName); note.recalculateCosts(economicPolicy, userId, pkgName); - note.setNewAffordability( - isAffordableLocked(getBalanceLocked(userId, pkgName), + note.setNewAffordability(isVip + || isAffordableLocked(getBalanceLocked(userId, pkgName), note.getCachedModifiedPrice(), note.getCtp())); mIrs.postAffordabilityChanged(userId, pkgName, note); // Update ongoing alarm @@ -1203,11 +1258,12 @@ class Agent { if (actionAffordabilityNotes != null && actionAffordabilityNotes.size() > 0) { final long newBalance = getBalanceLocked(userId, pkgName); + final boolean isVip = mIrs.isVip(userId, pkgName); for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); - final boolean isAffordable = isAffordableLocked( + final boolean isAffordable = isVip || isAffordableLocked( newBalance, note.getCachedModifiedPrice(), note.getCtp()); if (note.isCurrentlyAffordable() != isAffordable) { note.setNewAffordability(isAffordable); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java index a46430feb688..aa66e92a091f 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java @@ -149,7 +149,6 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { private long mHardSatiatedConsumptionLimit; private final KeyValueListParser mParser = new KeyValueListParser(','); - private final InternalResourceService mInternalResourceService; private final Injector mInjector; private final SparseArray<Action> mActions = new SparseArray<>(); @@ -157,7 +156,6 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { AlarmManagerEconomicPolicy(InternalResourceService irs, Injector injector) { super(irs); - mInternalResourceService = irs; mInjector = injector; loadConstants("", null); } @@ -165,14 +163,14 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { @Override void setup(@NonNull DeviceConfig.Properties properties) { super.setup(properties); - ContentResolver resolver = mInternalResourceService.getContext().getContentResolver(); + ContentResolver resolver = mIrs.getContext().getContentResolver(); loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_ALARM_MANAGER_CONSTANTS), properties); } @Override long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) { - if (mInternalResourceService.isPackageExempted(userId, pkgName)) { + if (mIrs.isPackageExempted(userId, pkgName)) { return mMinSatiatedBalanceExempted; } // TODO: take other exemptions into account diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java index 0937e7ba4055..564ffb9c4169 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -169,9 +169,11 @@ public abstract class EconomicPolicy { } } + protected final InternalResourceService mIrs; private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS]; EconomicPolicy(@NonNull InternalResourceService irs) { + mIrs = irs; for (int mId : getCostModifiers()) { initModifier(mId, irs); } @@ -240,7 +242,7 @@ public abstract class EconomicPolicy { @NonNull final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) { final Action action = getAction(actionId); - if (action == null) { + if (action == null || mIrs.isVip(userId, pkgName)) { return new Cost(0, 0); } long ctp = action.costToProduce; diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java new file mode 100644 index 000000000000..da544bb6a2eb --- /dev/null +++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.tare; + +import android.annotation.NonNull; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.os.UserHandle; + +/** POJO to cache only the information about installed packages that TARE cares about. */ +class InstalledPackageInfo { + static final int NO_UID = -1; + + public final int uid; + public final String packageName; + public final boolean hasCode; + + InstalledPackageInfo(@NonNull PackageInfo packageInfo) { + final ApplicationInfo applicationInfo = packageInfo.applicationInfo; + this.uid = applicationInfo == null ? NO_UID : applicationInfo.uid; + this.packageName = packageInfo.packageName; + this.hasCode = applicationInfo != null && applicationInfo.hasCode(); + } +} 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 6d5c16021ea9..7a7d669ae229 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -48,6 +48,7 @@ import android.os.Handler; import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -131,7 +132,7 @@ public class InternalResourceService extends SystemService { @NonNull @GuardedBy("mLock") - private final List<PackageInfo> mPkgCache = new ArrayList<>(); + private final List<InstalledPackageInfo> mPkgCache = new ArrayList<>(); /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */ @GuardedBy("mLock") @@ -149,6 +150,9 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private ArraySet<String> mExemptedApps = new ArraySet<>(); + @GuardedBy("mLock") + private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>(); + private volatile boolean mIsEnabled; private volatile int mBootPhase; private volatile boolean mExemptListLoaded; @@ -303,7 +307,7 @@ public class InternalResourceService extends SystemService { } @NonNull - List<PackageInfo> getInstalledPackages() { + List<InstalledPackageInfo> getInstalledPackages() { synchronized (mLock) { return mPkgCache; } @@ -311,13 +315,12 @@ public class InternalResourceService extends SystemService { /** Returns the installed packages for the specified user. */ @NonNull - List<PackageInfo> getInstalledPackages(final int userId) { - final List<PackageInfo> userPkgs = new ArrayList<>(); + List<InstalledPackageInfo> getInstalledPackages(final int userId) { + final List<InstalledPackageInfo> userPkgs = new ArrayList<>(); synchronized (mLock) { for (int i = 0; i < mPkgCache.size(); ++i) { - final PackageInfo packageInfo = mPkgCache.get(i); - if (packageInfo.applicationInfo != null - && UserHandle.getUserId(packageInfo.applicationInfo.uid) == userId) { + final InstalledPackageInfo packageInfo = mPkgCache.get(i); + if (UserHandle.getUserId(packageInfo.uid) == userId) { userPkgs.add(packageInfo); } } @@ -369,6 +372,21 @@ public class InternalResourceService extends SystemService { return UserHandle.isCore(getUid(userId, pkgName)); } + boolean isVip(final int userId, @NonNull String pkgName) { + synchronized (mLock) { + final Boolean override = mVipOverrides.get(userId, pkgName); + if (override != null) { + return override; + } + } + if (isSystem(userId, pkgName)) { + // The government, I mean the system, can create ARCs as it needs to in order to + // operate. + return true; + } + return false; + } + void onBatteryLevelChanged() { synchronized (mLock) { final int newBatteryLevel = getCurrentBatteryLevel(); @@ -451,7 +469,7 @@ public class InternalResourceService extends SystemService { mPackageToUidCache.add(userId, pkgName, uid); } synchronized (mLock) { - mPkgCache.add(packageInfo); + mPkgCache.add(new InstalledPackageInfo(packageInfo)); mUidToPackageCache.add(uid, pkgName); // TODO: only do this when the user first launches the app (app leaves stopped state) mAgent.grantBirthrightLocked(userId, pkgName); @@ -471,9 +489,10 @@ public class InternalResourceService extends SystemService { } synchronized (mLock) { mUidToPackageCache.remove(uid, pkgName); + mVipOverrides.delete(userId, pkgName); for (int i = 0; i < mPkgCache.size(); ++i) { - PackageInfo pkgInfo = mPkgCache.get(i); - if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId + final InstalledPackageInfo pkgInfo = mPkgCache.get(i); + if (UserHandle.getUserId(pkgInfo.uid) == userId && pkgName.equals(pkgInfo.packageName)) { mPkgCache.remove(i); break; @@ -496,20 +515,24 @@ public class InternalResourceService extends SystemService { void onUserAdded(final int userId) { synchronized (mLock) { - mPkgCache.addAll( - mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId)); + final List<PackageInfo> pkgs = + mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); + for (int i = pkgs.size() - 1; i >= 0; --i) { + mPkgCache.add(new InstalledPackageInfo(pkgs.get(i))); + } mAgent.grantBirthrightsLocked(userId); } } void onUserRemoved(final int userId) { synchronized (mLock) { + mVipOverrides.delete(userId); ArrayList<String> removedPkgs = new ArrayList<>(); for (int i = mPkgCache.size() - 1; i >= 0; --i) { - PackageInfo pkgInfo = mPkgCache.get(i); - if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) { + final InstalledPackageInfo pkgInfo = mPkgCache.get(i); + if (UserHandle.getUserId(pkgInfo.uid) == userId) { removedPkgs.add(pkgInfo.packageName); - mUidToPackageCache.remove(pkgInfo.applicationInfo.uid); + mUidToPackageCache.remove(pkgInfo.uid); mPkgCache.remove(i); break; } @@ -659,8 +682,11 @@ public class InternalResourceService extends SystemService { LocalServices.getService(UserManagerInternal.class); final int[] userIds = userManagerInternal.getUserIds(); for (int userId : userIds) { - mPkgCache.addAll( - mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId)); + final List<PackageInfo> pkgs = + mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); + for (int i = pkgs.size() - 1; i >= 0; --i) { + mPkgCache.add(new InstalledPackageInfo(pkgs.get(i))); + } } } @@ -881,6 +907,15 @@ public class InternalResourceService extends SystemService { Binder.restoreCallingIdentity(identityToken); } } + + @Override + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return (new TareShellCommand(InternalResourceService.this)).exec( + this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), + args); + } } private final class LocalService implements EconomyManagerInternal { @@ -932,9 +967,9 @@ public class InternalResourceService extends SystemService { if (!mIsEnabled) { return true; } - if (isSystem(userId, pkgName)) { + if (isVip(userId, pkgName)) { // The government, I mean the system, can create ARCs as it needs to in order to - // operate. + // allow VIPs to operate. return true; } // TODO: take temp-allowlist into consideration @@ -960,7 +995,7 @@ public class InternalResourceService extends SystemService { if (!mIsEnabled) { return FOREVER_MS; } - if (isSystem(userId, pkgName)) { + if (isVip(userId, pkgName)) { return FOREVER_MS; } long totalCostPerSecond = 0; @@ -1131,6 +1166,47 @@ public class InternalResourceService extends SystemService { } } + // Shell command infrastructure + int executeClearVip(@NonNull PrintWriter pw) { + synchronized (mLock) { + final SparseSetArray<String> changedPkgs = new SparseSetArray<>(); + for (int u = mVipOverrides.numMaps() - 1; u >= 0; --u) { + final int userId = mVipOverrides.keyAt(u); + + for (int p = mVipOverrides.numElementsForKeyAt(u) - 1; p >= 0; --p) { + changedPkgs.add(userId, mVipOverrides.keyAt(u, p)); + } + } + mVipOverrides.clear(); + if (mIsEnabled) { + mAgent.onVipStatusChangedLocked(changedPkgs); + } + } + pw.println("Cleared all VIP statuses"); + return TareShellCommand.COMMAND_SUCCESS; + } + + int executeSetVip(@NonNull PrintWriter pw, + int userId, @NonNull String pkgName, @Nullable Boolean newVipState) { + final boolean changed; + synchronized (mLock) { + final boolean wasVip = isVip(userId, pkgName); + if (newVipState == null) { + mVipOverrides.delete(userId, pkgName); + } else { + mVipOverrides.add(userId, pkgName, newVipState); + } + changed = isVip(userId, pkgName) != wasVip; + if (mIsEnabled && changed) { + mAgent.onVipStatusChangedLocked(userId, pkgName); + } + } + pw.println(appToString(userId, pkgName) + " VIP status set to " + newVipState + "." + + " Final VIP state changed? " + changed); + return TareShellCommand.COMMAND_SUCCESS; + } + + // Dump infrastructure private static void dumpHelp(PrintWriter pw) { pw.println("Resource Economy (economy) dump options:"); pw.println(" [-h|--help] [package] ..."); @@ -1168,6 +1244,29 @@ public class InternalResourceService extends SystemService { pw.print("Exempted apps", mExemptedApps); pw.println(); + boolean printedVips = false; + pw.println(); + pw.print("VIPs:"); + for (int u = 0; u < mVipOverrides.numMaps(); ++u) { + final int userId = mVipOverrides.keyAt(u); + + for (int p = 0; p < mVipOverrides.numElementsForKeyAt(u); ++p) { + final String pkgName = mVipOverrides.keyAt(u, p); + + printedVips = true; + pw.println(); + pw.print(appToString(userId, pkgName)); + pw.print("="); + pw.print(mVipOverrides.valueAt(u, p)); + } + } + if (printedVips) { + pw.println(); + } else { + pw.print(" None"); + } + pw.println(); + pw.println(); mCompleteEconomicPolicy.dump(pw); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java index e7db1adc859e..03c5fdd63250 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java @@ -151,7 +151,6 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { private long mHardSatiatedConsumptionLimit; private final KeyValueListParser mParser = new KeyValueListParser(','); - private final InternalResourceService mInternalResourceService; private final Injector mInjector; private final SparseArray<Action> mActions = new SparseArray<>(); @@ -159,7 +158,6 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector) { super(irs); - mInternalResourceService = irs; mInjector = injector; loadConstants("", null); } @@ -167,14 +165,14 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { @Override void setup(@NonNull DeviceConfig.Properties properties) { super.setup(properties); - ContentResolver resolver = mInternalResourceService.getContext().getContentResolver(); + final ContentResolver resolver = mIrs.getContext().getContentResolver(); loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_JOB_SCHEDULER_CONSTANTS), properties); } @Override long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) { - if (mInternalResourceService.isPackageExempted(userId, pkgName)) { + if (mIrs.isPackageExempted(userId, pkgName)) { return mMinSatiatedBalanceExempted; } // TODO: take other exemptions into account diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java index 8f7657e6c414..ed915cd88ebe 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java @@ -22,7 +22,6 @@ import static com.android.server.tare.TareUtils.appToString; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.pm.PackageInfo; import android.os.Environment; import android.os.UserHandle; import android.util.ArraySet; @@ -210,11 +209,11 @@ public class Scribe { mRemainingConsumableCakes = 0; final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>(); - final List<PackageInfo> installedPackages = mIrs.getInstalledPackages(); + final List<InstalledPackageInfo> installedPackages = mIrs.getInstalledPackages(); for (int i = 0; i < installedPackages.size(); ++i) { - final PackageInfo packageInfo = installedPackages.get(i); - if (packageInfo.applicationInfo != null) { - final int userId = UserHandle.getUserId(packageInfo.applicationInfo.uid); + final InstalledPackageInfo packageInfo = installedPackages.get(i); + if (packageInfo.uid != InstalledPackageInfo.NO_UID) { + final int userId = UserHandle.getUserId(packageInfo.uid); ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId); if (pkgsForUser == null) { pkgsForUser = new ArraySet<>(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java b/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java new file mode 100644 index 000000000000..5e380b408d01 --- /dev/null +++ b/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.tare; + +import android.Manifest; +import android.annotation.NonNull; +import android.content.pm.PackageManager; +import android.os.Binder; + +import com.android.modules.utils.BasicShellCommandHandler; + +import java.io.PrintWriter; + +/** + * Shell command handler for TARE. + */ +public class TareShellCommand extends BasicShellCommandHandler { + static final int COMMAND_ERROR = -1; + static final int COMMAND_SUCCESS = 0; + + private final InternalResourceService mIrs; + + public TareShellCommand(@NonNull InternalResourceService irs) { + mIrs = irs; + } + + @Override + public int onCommand(String cmd) { + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd != null ? cmd : "") { + case "clear-vip": + return runClearVip(pw); + case "set-vip": + return runSetVip(pw); + default: + return handleDefaultCommands(cmd); + } + } catch (Exception e) { + pw.println("Exception: " + e); + } + return COMMAND_ERROR; + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + + pw.println("TARE commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" clear-vip"); + pw.println(" Clears all VIP settings resulting from previous calls using `set-vip` and"); + pw.println(" resets them all to default."); + pw.println(" set-vip <USER_ID> <PACKAGE> <true|false|default>"); + pw.println(" Designate the app as a Very Important Package or not. A VIP is allowed to"); + pw.println(" do as much work as it wants, regardless of TARE state."); + pw.println(" The user ID must be an explicit user ID. USER_ALL, CURRENT, etc. are not"); + pw.println(" supported."); + pw.println(); + } + + private void checkPermission(@NonNull String operation) throws Exception { + final int perm = mIrs.getContext() + .checkCallingOrSelfPermission(Manifest.permission.CHANGE_APP_IDLE_STATE); + if (perm != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Uid " + Binder.getCallingUid() + + " not permitted to " + operation); + } + } + + private int runClearVip(@NonNull PrintWriter pw) throws Exception { + checkPermission("clear vip"); + + final long ident = Binder.clearCallingIdentity(); + try { + return mIrs.executeClearVip(pw); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private int runSetVip(@NonNull PrintWriter pw) throws Exception { + checkPermission("modify vip"); + + final int userId = Integer.parseInt(getNextArgRequired()); + final String pkgName = getNextArgRequired(); + final String vipState = getNextArgRequired(); + final Boolean isVip = "default".equals(vipState) ? null : Boolean.valueOf(vipState); + + final long ident = Binder.clearCallingIdentity(); + try { + return mIrs.executeSetVip(pw, userId, pkgName, isVip); + } finally { + Binder.restoreCallingIdentity(ident); + } + } +} diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING index 682d3ab6dd60..fd571c95f568 100644 --- a/core/TEST_MAPPING +++ b/core/TEST_MAPPING @@ -10,9 +10,6 @@ "include-filter": "com.android.internal.inputmethod" }, { - "include-filter": "android.accessibilityService.cts" - }, - { "exclude-annotation": "androidx.test.filters.FlakyTest" } ], diff --git a/core/api/current.txt b/core/api/current.txt index ea2ae0c9e15f..4734e8a75de6 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -23189,9 +23189,10 @@ package android.media { public abstract static class MediaRouter2.RouteCallback { ctor public MediaRouter2.RouteCallback(); - method public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>); - method public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>); - method public void onRoutesRemoved(@NonNull java.util.List<android.media.MediaRoute2Info>); + method @Deprecated public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>); + method @Deprecated public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>); + method @Deprecated public void onRoutesRemoved(@NonNull java.util.List<android.media.MediaRoute2Info>); + method public void onRoutesUpdated(@NonNull java.util.List<android.media.MediaRoute2Info>); } public class MediaRouter2.RoutingController { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6c941e751b9e..c87ea2adbd77 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -798,6 +798,7 @@ package android.content.pm { field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f; field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L + field public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L; // 0xc6fb886L field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2 } diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java index 47bec618cfd9..24fe0dbe5760 100644 --- a/core/java/android/attention/AttentionManagerInternal.java +++ b/core/java/android/attention/AttentionManagerInternal.java @@ -83,11 +83,11 @@ public abstract class AttentionManagerInternal { } /** Internal interface for proximity callback. */ - public abstract static class ProximityUpdateCallbackInternal { + public interface ProximityUpdateCallbackInternal { /** * @param distance the estimated distance of the user (in meter) * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive. */ - public abstract void onProximityUpdate(double distance); + void onProximityUpdate(double distance); } } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index a3d595ef6575..e92be7cb64e2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1109,6 +1109,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f; /** + * Enables the use of split screen aspect ratio. This allows an app to use all the available + * space in split mode avoiding letterboxing. + * @hide + */ + @ChangeId + @Disabled + @Overridable + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L; + + /** * Compares activity window layout min width/height with require space for multi window to * determine if it can be put into multi window mode. */ @@ -1317,8 +1328,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Returns true if the activity has maximum or minimum aspect ratio. * @hide */ - public boolean hasFixedAspectRatio(@ScreenOrientation int orientation) { - return getMaxAspectRatio() != 0 || getMinAspectRatio(orientation) != 0; + public boolean hasFixedAspectRatio() { + return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** @@ -1460,30 +1471,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** - * Returns the min aspect ratio of this activity. - * - * This takes into account the minimum aspect ratio as defined in the app's manifest and - * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO. - * - * In the rare cases where the manifest minimum aspect ratio is required, use - * {@code getManifestMinAspectRatio}. + * Returns the min aspect ratio of this activity as defined in the manifest file. * @hide */ - public float getMinAspectRatio(@ScreenOrientation int orientation) { - if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || ( - isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY) - && !isFixedOrientationPortrait(orientation))) { - return mMinAspectRatio; - } - - if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) { - return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio); - } - - if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) { - return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio); - } - + public float getMinAspectRatio() { return mMinAspectRatio; } @@ -1512,7 +1503,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } } - private boolean isChangeEnabled(long changeId) { + /** + * Checks if a changeId is enabled for the current user + * @param changeId The changeId to verify + * @return True of the changeId is enabled + * @hide + */ + public boolean isChangeEnabled(long changeId) { return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName, UserHandle.getUserHandleForUid(applicationInfo.uid)); } @@ -1633,12 +1630,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (getMaxAspectRatio() != 0) { pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio()); } - final float minAspectRatio = getMinAspectRatio(screenOrientation); + final float minAspectRatio = getMinAspectRatio(); if (minAspectRatio != 0) { pw.println(prefix + "minAspectRatio=" + minAspectRatio); - if (getManifestMinAspectRatio() != minAspectRatio) { - pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio()); - } } if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 7092e43596ec..7247ef77afb4 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -29,6 +29,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.CryptoObject; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; @@ -674,6 +675,45 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** + * Forwards BiometricStateListener to FaceService. + * + * @param listener new BiometricStateListener being added + * @hide + */ + public void registerBiometricStateListener(@NonNull BiometricStateListener listener) { + try { + mService.registerBiometricStateListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Adds a callback that gets called when the service registers all of the face + * authenticators (HALs). + * + * If the face authenticators are already registered when the callback is added, the + * callback is invoked immediately. + * + * The callback is automatically removed after it's invoked. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void addAuthenticatorsRegisteredCallback( + IFaceAuthenticatorsRegisteredCallback callback) { + if (mService != null) { + try { + mService.addAuthenticatorsRegisteredCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!"); + } + } + + /** * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) diff --git a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl new file mode 100644 index 000000000000..78f978d21ed7 --- /dev/null +++ b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl @@ -0,0 +1,35 @@ +/* + * 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 android.hardware.face; + +import android.hardware.face.FaceSensorPropertiesInternal; +import java.util.List; + +/** + * Callback to notify FaceManager that FaceService has registered all of the + * face authenticators (HALs). + * See {@link android.hardware.face.IFaceService#registerAuthenticators}. + * + * @hide + */ +oneway interface IFaceAuthenticatorsRegisteredCallback { + /** + * Notifies FaceManager that all of the face authenticators have been registered. + * + * @param sensors A consolidated list of sensor properties for all of the authenticators. + */ + void onAllAuthenticatorsRegistered(in List<FaceSensorPropertiesInternal> sensors); +} diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 369248edd580..9b56f43a0f22 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -17,9 +17,11 @@ package android.hardware.face; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; @@ -163,4 +165,11 @@ interface IFaceService { // hidlSensors must be non-null and empty. See AuthService.java @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors); + + // Adds a callback which gets called when the service registers all of the face + // authenticators. The callback is automatically removed after it's invoked. + void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback); + + // Registers BiometricStateListener. + void registerBiometricStateListener(IBiometricStateListener listener); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index cc7ed183ed64..1ba9a0471c88 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -202,8 +202,10 @@ interface IFingerprintService { void setSidefpsController(in ISidefpsController controller); // Registers BiometricStateListener. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerBiometricStateListener(IBiometricStateListener listener); // Sends a power button pressed event to all listeners. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") oneway void onPowerPressed(); } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 2483f99a07ec..b7adcb84102d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -755,6 +755,13 @@ public final class DeviceConfig { public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native"; /** + * Namespace for Vendor System Native Boot related features. + * + * @hide + */ + public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE_BOOT = "vendor_system_native_boot"; + + /** * Namespace for memory safety related features (e.g. MTE) * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 05b3925fcb9f..a7c72730a89f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5486,6 +5486,15 @@ public final class Settings { public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled"; /** + * Whether desktop mode is enabled or not. + * 0 = off + * 1 = on + * @hide + */ + @Readable + public static final String DESKTOP_MODE = "desktop_mode"; + + /** * IMPORTANT: If you add a new public settings you also have to add it to * PUBLIC_SETTINGS below. If the new setting is hidden you have to add * it to PRIVATE_SETTINGS below. Also add a validator that can validate @@ -5614,6 +5623,7 @@ public final class Settings { PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT); PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE); PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT); + PRIVATE_SETTINGS.add(DESKTOP_MODE); } /** @@ -17688,20 +17698,13 @@ public final class Settings { "wear_activity_auto_resume_timeout_set_by_user"; /** - * The maximum times that we are allowed to reset the activity auto-resume timeout - * timer. - * @hide - */ - public static final String WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT = - "wear_activity_auto_resume_timeout_max_reset_count"; - - /** * If burn in protection is enabled. * @hide */ public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection"; /** + * Whether the device has combined location setting enabled. * @hide */ diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java index 72341453a1f4..ab71459ed51e 100644 --- a/core/java/android/service/voice/HotwordDetectedResult.java +++ b/core/java/android/service/voice/HotwordDetectedResult.java @@ -95,6 +95,16 @@ public final class HotwordDetectedResult implements Parcelable { /** Limits the max value for the triggered audio channel. */ private static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE = 63; + /** + * The bundle key for proximity value + * + * TODO(b/238896013): Move the proximity logic out of bundle to proper API. + * + * @hide + */ + public static final String EXTRA_PROXIMITY_METERS = + "android.service.voice.extra.PROXIMITY_METERS"; + /** Confidence level in the trigger outcome. */ @HotwordConfidenceLevelValue private final int mConfidenceLevel; @@ -197,6 +207,14 @@ public final class HotwordDetectedResult implements Parcelable { * <p>The use of this method is discouraged, and support for it will be removed in future * versions of Android. * + * <p>After the trigger happens, a special case of proximity-related extra, with the key of + * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), + * will be stored to enable proximity logic. The proximity meters is provided by the system, + * on devices that support detecting proximity of nearby users, to help disambiguate which + * nearby device should respond. When the proximity is unknown, the proximity value will not + * be stored. This mapping will be excluded from the max bundle size calculation because this + * mapping is included after the result is returned from the hotword detector service. + * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. */ @@ -315,8 +333,23 @@ public final class HotwordDetectedResult implements Parcelable { "audioChannel"); } if (!mExtras.isEmpty()) { - Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(), - "extras"); + // Remove the proximity key from the bundle before checking the bundle size. The + // proximity value is added after the privileged module and can avoid the + // maxBundleSize limitation. + if (mExtras.containsKey(EXTRA_PROXIMITY_METERS)) { + double proximityMeters = mExtras.getDouble(EXTRA_PROXIMITY_METERS); + mExtras.remove(EXTRA_PROXIMITY_METERS); + // Skip checking parcelable size if the new bundle size is 0. Newly empty bundle + // has parcelable size of 4, but the default bundle has parcelable size of 0. + if (mExtras.size() > 0) { + Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, + getMaxBundleSize(), "extras"); + } + mExtras.putDouble(EXTRA_PROXIMITY_METERS, proximityMeters); + } else { + Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, + getMaxBundleSize(), "extras"); + } } } @@ -513,6 +546,14 @@ public final class HotwordDetectedResult implements Parcelable { * <p>The use of this method is discouraged, and support for it will be removed in future * versions of Android. * + * <p>After the trigger happens, a special case of proximity-related extra, with the key of + * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), + * will be stored to enable proximity logic. The proximity meters is provided by the system, + * on devices that support detecting proximity of nearby users, to help disambiguate which + * nearby device should respond. When the proximity is unknown, the proximity value will not + * be stored. This mapping will be excluded from the max bundle size calculation because this + * mapping is included after the result is returned from the hotword detector service. + * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. */ @@ -813,6 +854,14 @@ public final class HotwordDetectedResult implements Parcelable { * <p>The use of this method is discouraged, and support for it will be removed in future * versions of Android. * + * <p>After the trigger happens, a special case of proximity-related extra, with the key of + * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), + * will be stored to enable proximity logic. The proximity meters is provided by the system, + * on devices that support detecting proximity of nearby users, to help disambiguate which + * nearby device should respond. When the proximity is unknown, the proximity value will not + * be stored. This mapping will be excluded from the max bundle size calculation because this + * mapping is included after the result is returned from the hotword detector service. + * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. */ @@ -882,10 +931,10 @@ public final class HotwordDetectedResult implements Parcelable { } @DataClass.Generated( - time = 1625541522353L, + time = 1658357814396L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java", - inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 4fbf4b528fe9..bc8822cfb3e9 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -1153,8 +1153,8 @@ public abstract class WallpaperService extends Service { mLayout.surfaceInsets.set(0, 0, 0, 0); } final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight, - View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl, - mInsetsState, mTempControls, mSyncSeqIdBundle); + View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration, + mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle); final int transformHint = SurfaceControl.rotationToBufferTransform( (mDisplayInstallOrientation + mDisplay.getRotation()) % 4); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 3016473fa040..afcec6639dcf 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -75,41 +75,42 @@ interface IWindowSession { * @param requestedWidth The width the window wants to be. * @param requestedHeight The height the window wants to be. * @param viewVisibility Window root view's visibility. - * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}, - * {@link WindowManagerGlobal#RELAYOUT_DEFER_SURFACE_DESTROY}. - * @param outFrame Rect in which is placed the new position/size on - * screen. - * @param outContentInsets Rect in which is placed the offsets from - * <var>outFrame</var> in which the content of the window should be - * placed. This can be used to modify the window layout to ensure its - * contents are visible to the user, taking into account system windows - * like the status bar or a soft keyboard. - * @param outVisibleInsets Rect in which is placed the offsets from - * <var>outFrame</var> in which the window is actually completely visible - * to the user. This can be used to temporarily scroll the window's - * contents to make sure the user can see it. This is different than - * <var>outContentInsets</var> in that these insets change transiently, - * so complex relayout of the window should not happen based on them. - * @param outOutsets Rect in which is placed the dead area of the screen that we would like to - * treat as real display. Example of such area is a chin in some models of wearable devices. - * @param outBackdropFrame Rect which is used draw the resizing background during a resize - * operation. + * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}. + * @param seq The calling sequence of {@link #relayout} and {@link #relayoutAsync}. + * @param lastSyncSeqId The last SyncSeqId that the client applied. + * @param outFrames The window frames used by the client side for layout. * @param outMergedConfiguration New config container that holds global, override and merged - * config for window, if it is now becoming visible and the merged configuration has changed - * since it was last displayed. - * @param outSurface Object in which is placed the new display surface. + * config for window, if it is now becoming visible and the merged + * config has changed since it was last displayed. + * @param outSurfaceControl Object in which is placed the new display surface. * @param insetsState The current insets state in the system. - * - * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS}, - * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}. + * @param activeControls Objects which allow controlling {@link InsetsSource}s. + * @param bundle A temporary object to obtain the latest SyncSeqId. + * @return int Result flags, defined in {@link WindowManagerGlobal}. */ int relayout(IWindow window, in WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, - int flags, out ClientWindowFrames outFrames, + int flags, int seq, int lastSyncSeqId, out ClientWindowFrames outFrames, out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl, out InsetsState insetsState, out InsetsSourceControl[] activeControls, out Bundle bundle); + /** + * Similar to {@link #relayout} but this is an oneway method which doesn't return anything. + * + * @param window The window being modified. + * @param attrs If non-null, new attributes to apply to the window. + * @param requestedWidth The width the window wants to be. + * @param requestedHeight The height the window wants to be. + * @param viewVisibility Window root view's visibility. + * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}. + * @param seq The calling sequence of {@link #relayout} and {@link #relayoutAsync}. + * @param lastSyncSeqId The last SyncSeqId that the client applied. + */ + oneway void relayoutAsync(IWindow window, in WindowManager.LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq, + int lastSyncSeqId); + /* * Notify the window manager that an application is relaunching and * windows should be prepared for replacement. diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index d63c25a09382..5236fe772a7b 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -176,7 +176,9 @@ public class InsetsSourceConsumer { // If we have a new leash, make sure visibility is up-to-date, even though we // didn't want to run an animation above. - applyRequestedVisibilityToControl(); + if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) { + applyRequestedVisibilityToControl(); + } // Remove the surface that owned by last control when it lost. if (!requestedVisible && lastControl == null) { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 70a5eda1d03e..8f9c5fe2b87f 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -119,10 +119,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall final int[] mLocation = new int[2]; @UnsupportedAppUsage - // Used to ensure the Surface remains valid between SurfaceHolder#lockCanvas and - // SurfaceHolder#unlockCanvasAndPost calls. This prevents SurfaceView from destroying or - // invalidating the Surface. This means this lock should be acquired when destroying the - // BlastBufferQueue. final ReentrantLock mSurfaceLock = new ReentrantLock(); @UnsupportedAppUsage final Surface mSurface = new Surface(); // Current surface in use @@ -727,18 +723,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void releaseSurfaces(boolean releaseSurfacePackage) { mSurfaceAlpha = 1f; - try { - mSurfaceLock.lock(); - mSurface.destroy(); + mSurface.destroy(); + + synchronized (mSurfaceControlLock) { if (mBlastBufferQueue != null) { mBlastBufferQueue.destroy(); mBlastBufferQueue = null; } - } finally { - mSurfaceLock.unlock(); - } - synchronized (mSurfaceControlLock) { final Transaction transaction = new Transaction(); if (mSurfaceControl != null) { transaction.remove(mSurfaceControl); @@ -1240,21 +1232,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall .build(); } - mTransformHint = viewRoot.getBufferTransformHint(); - mBlastSurfaceControl.setTransformHint(mTransformHint); - // Always recreate the IGBP for compatibility. This can be optimized in the future but // the behavior change will need to be gated by SDK version. - try { - mSurfaceLock.lock(); - if (mBlastBufferQueue != null) { - mBlastBufferQueue.destroy(); - } - mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */); - mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); - } finally { - mSurfaceLock.unlock(); + if (mBlastBufferQueue != null) { + mBlastBufferQueue.destroy(); } + mTransformHint = viewRoot.getBufferTransformHint(); + mBlastSurfaceControl.setTransformHint(mTransformHint); + + mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */); + mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback); } @@ -1827,14 +1814,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // so the next connect will always work if we end up reusing // the surface. if (mSurface.isValid()) { - // We need to grab this lock since mSurface.forceScopedDisconnect - // will free buffers from the queue. - try { - mSurfaceLock.lock(); - mSurface.forceScopedDisconnect(); - } finally { - mSurfaceLock.unlock(); - } + mSurface.forceScopedDisconnect(); } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f79f0d49959d..12bc169153a4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -75,6 +75,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; @@ -707,6 +708,8 @@ public final class ViewRootImpl implements ViewParent, final Rect mPendingBackDropFrame = new Rect(); boolean mPendingAlwaysConsumeSystemBars; + private int mRelayoutSeq; + private final Rect mWinFrameInScreen = new Rect(); private final InsetsState mTempInsets = new InsetsState(); private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE]; private final WindowConfiguration mTempWinConfig = new WindowConfiguration(); @@ -3364,20 +3367,6 @@ public final class ViewRootImpl implements ViewParent, } } } else { - // If a relayout isn't going to happen, we still need to check if this window can draw - // when mCheckIfCanDraw is set. This is because it means we had a sync in the past, but - // have not been told by WMS that the sync is complete and that we can continue to draw - if (mCheckIfCanDraw) { - try { - cancelDraw = mWindowSession.cancelDraw(mWindow); - cancelReason = "wm_sync"; - if (DEBUG_BLAST) { - Log.d(mTag, "cancelDraw returned " + cancelDraw); - } - } catch (RemoteException e) { - } - } - // Not the first pass and no window/insets/visibility change but the window // may have moved and we need check that and if so to update the left and right // in the attach info. We translate only the window frame since on window move @@ -3386,6 +3375,20 @@ public final class ViewRootImpl implements ViewParent, maybeHandleWindowMove(frame); } + if (!mRelayoutRequested && mCheckIfCanDraw) { + // We had a sync previously, but we didn't call IWindowSession#relayout in this + // traversal. So we don't know if the sync is complete that we can continue to draw. + // Here invokes cancelDraw to obtain the information. + try { + cancelDraw = mWindowSession.cancelDraw(mWindow); + cancelReason = "wm_sync"; + if (DEBUG_BLAST) { + Log.d(mTag, "cancelDraw returned " + cancelDraw); + } + } catch (RemoteException e) { + } + } + if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { // If the surface has been replaced, there's a chance the bounds layer is not parented // to the new layer. When updating bounds layer, also reparent to the main VRI @@ -6403,6 +6406,12 @@ public final class ViewRootImpl implements ViewParent, // Make sure the fallback event policy sees all keys that will be // delivered to the view hierarchy. mFallbackEventHandler.preDispatchKeyEvent(event); + + // Reset last tracked MotionEvent click toolType. + if (event.getAction() == KeyEvent.ACTION_DOWN) { + mLastClickToolType = MotionEvent.TOOL_TYPE_UNKNOWN; + } + return FORWARD; } @@ -8151,7 +8160,43 @@ public final class ViewRootImpl implements ViewParent, private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { - mRelayoutRequested = true; + final WindowConfiguration winConfigFromAm = getConfiguration().windowConfiguration; + final WindowConfiguration winConfigFromWm = + mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration; + final WindowConfiguration winConfig = getCompatWindowConfiguration(); + final int measuredWidth = mView.getMeasuredWidth(); + final int measuredHeight = mView.getMeasuredHeight(); + final boolean relayoutAsync; + if (LOCAL_LAYOUT && !mFirst && viewVisibility == mViewVisibility + && mWindowAttributes.type != TYPE_APPLICATION_STARTING + && mSyncSeqId <= mLastSyncSeqId + && winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) { + final InsetsState state = mInsetsController.getState(); + final Rect displayCutoutSafe = mTempRect; + state.getDisplayCutoutSafe(displayCutoutSafe); + mWindowLayout.computeFrames(mWindowAttributes.forRotation(winConfig.getRotation()), + state, displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(), + measuredWidth, measuredHeight, mInsetsController.getRequestedVisibilities(), + 1f /* compatScale */, mTmpFrames); + mWinFrameInScreen.set(mTmpFrames.frame); + if (mTranslator != null) { + mTranslator.translateRectInAppWindowToScreen(mWinFrameInScreen); + } + + // If the position and the size of the frame are both changed, it will trigger a BLAST + // sync, and we still need to call relayout to obtain the syncSeqId. Otherwise, we just + // need to send attributes via relayoutAsync. + final Rect oldFrame = mWinFrame; + final Rect newFrame = mTmpFrames.frame; + final boolean positionChanged = + newFrame.top != oldFrame.top || newFrame.left != oldFrame.left; + final boolean sizeChanged = + newFrame.width() != oldFrame.width() || newFrame.height() != oldFrame.height(); + relayoutAsync = !positionChanged || !sizeChanged; + } else { + relayoutAsync = false; + } + float appScale = mAttachInfo.mApplicationScale; boolean restore = false; if (params != null && mTranslator != null) { @@ -8173,26 +8218,47 @@ public final class ViewRootImpl implements ViewParent, } } - final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f); - final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f); + final int requestedWidth = (int) (measuredWidth * appScale + 0.5f); + final int requestedHeight = (int) (measuredHeight * appScale + 0.5f); + int relayoutResult = 0; + mRelayoutSeq++; + if (relayoutAsync) { + mWindowSession.relayoutAsync(mWindow, params, + requestedWidth, requestedHeight, viewVisibility, + insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq, + mLastSyncSeqId); + } else { + relayoutResult = mWindowSession.relayout(mWindow, params, + requestedWidth, requestedHeight, viewVisibility, + insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq, + mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, + mTempInsets, mTempControls, mRelayoutBundle); + mRelayoutRequested = true; + final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid"); + if (maybeSyncSeqId > 0) { + mSyncSeqId = maybeSyncSeqId; + } + mWinFrameInScreen.set(mTmpFrames.frame); + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); + mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame); + mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame); + mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); + mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); + } + mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale; + mInsetsController.onStateChanged(mTempInsets); + mInsetsController.onControlsChanged(mTempControls); - int relayoutResult = mWindowSession.relayout(mWindow, params, - requestedWidth, requestedHeight, viewVisibility, - insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, - mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, - mTempControls, mRelayoutBundle); - final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid"); - if (maybeSyncSeqId > 0) { - mSyncSeqId = maybeSyncSeqId; + mPendingAlwaysConsumeSystemBars = + (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; } - mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale; final int transformHint = SurfaceControl.rotationToBufferTransform( (mDisplayInstallOrientation + mDisplay.getRotation()) % 4); - final WindowConfiguration winConfig = getCompatWindowConfiguration(); WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth, - requestedHeight, mTmpFrames.frame, mPendingDragResizing, mSurfaceSize); + requestedHeight, mWinFrameInScreen, mPendingDragResizing, mSurfaceSize); final boolean transformHintChanged = transformHint != mLastTransformHint; final boolean sizeChanged = !mLastSurfaceSize.equals(mSurfaceSize); @@ -8239,23 +8305,11 @@ public final class ViewRootImpl implements ViewParent, destroySurface(); } - mPendingAlwaysConsumeSystemBars = - (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0; - if (restore) { params.restore(); } - if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); - mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame); - mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame); - mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); - mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); - } setFrame(mTmpFrames.frame); - mInsetsController.onStateChanged(mTempInsets); - mInsetsController.onControlsChanged(mTempControls); return relayoutResult; } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index d55c838c3e53..1ec17d00e99a 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -286,10 +286,11 @@ public class WindowlessWindowManager implements IWindowSession { @Override public int relayout(IWindow window, WindowManager.LayoutParams inAttrs, - int requestedWidth, int requestedHeight, int viewFlags, int flags, - ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, - SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) { + int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, + int lastSyncSeqId, ClientWindowFrames outFrames, + MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl, + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, + Bundle outSyncSeqIdBundle) { final State state; synchronized (this) { state = mStateForWindow.get(window.asBinder()); @@ -309,15 +310,23 @@ public class WindowlessWindowManager implements IWindowSession { if (viewFlags == View.VISIBLE) { t.setOpaque(sc, isOpaque(attrs)).show(sc).apply(); - outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout"); + if (outSurfaceControl != null) { + outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout"); + } } else { t.hide(sc).apply(); - outSurfaceControl.release(); + if (outSurfaceControl != null) { + outSurfaceControl.release(); + } + } + if (outFrames != null) { + outFrames.frame.set(0, 0, attrs.width, attrs.height); + outFrames.displayFrame.set(outFrames.frame); } - outFrames.frame.set(0, 0, attrs.width, attrs.height); - outFrames.displayFrame.set(outFrames.frame); - mergedConfiguration.setConfiguration(mConfiguration, mConfiguration); + if (outMergedConfiguration != null) { + outMergedConfiguration.setConfiguration(mConfiguration, mConfiguration); + } if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0 && state.mInputChannelToken != null) { @@ -335,7 +344,7 @@ public class WindowlessWindowManager implements IWindowSession { } } - if (mInsetsState != null) { + if (outInsetsState != null && mInsetsState != null) { outInsetsState.set(mInsetsState); } @@ -343,6 +352,16 @@ public class WindowlessWindowManager implements IWindowSession { } @Override + public void relayoutAsync(IWindow window, WindowManager.LayoutParams inAttrs, + int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, + int lastSyncSeqId) { + relayout(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags, seq, + lastSyncSeqId, null /* outFrames */, null /* outMergedConfiguration */, + null /* outSurfaceControl */, null /* outInsetsState */, + null /* outActiveControls */, null /* outSyncSeqIdBundle */); + } + + @Override public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) { } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0c03d109f649..8d3cf6d890d2 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -18,7 +18,6 @@ package android.view.inputmethod; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; -import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS; import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE; import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; @@ -2086,15 +2085,6 @@ public final class InputMethodManager { Log.w(TAG, "Ignoring startStylusHandwriting: View's window does not have focus."); return; } - if (mServedInputConnection != null && getDelegate().hasActiveConnection(view)) { - // TODO (b/210039666): optimize CURSOR_UPDATE_IMMEDIATE. - // TODO (b/210039666): Pipe IME displayId from InputBindResult and use it here. - // instead of mDisplayId. - mServedInputConnection.requestCursorUpdatesFromImm( - CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR, - CURSOR_UPDATE_FILTER_EDITOR_BOUNDS, - mDisplayId); - } mServiceInvoker.startStylusHandwriting(mClient); // TODO(b/210039666): do we need any extra work for supporting non-native @@ -2341,9 +2331,6 @@ public final class InputMethodManager { editorInfo.packageName = view.getContext().getOpPackageName(); editorInfo.autofillId = view.getAutofillId(); editorInfo.fieldId = view.getId(); - synchronized (mH) { - editorInfo.setInitialToolType(mCurRootView.getLastClickToolType()); - } InputConnection ic = view.onCreateInputConnection(editorInfo); if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic); @@ -2383,6 +2370,8 @@ public final class InputMethodManager { startInputFlags |= StartInputFlags.INITIAL_CONNECTION; } + editorInfo.setInitialToolType(mCurRootView.getLastClickToolType()); + // Hook 'em up and let 'er rip. mCurrentEditorInfo = editorInfo.createCopyInternal(); // Store the previously served connection so that we can determine whether it is safe diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b233e5453c05..b21c5b35e24b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -226,6 +226,8 @@ public class Editor { final UndoInputFilter mUndoInputFilter = new UndoInputFilter(this); boolean mAllowUndo = true; + private int mLastToolType = MotionEvent.TOOL_TYPE_UNKNOWN; + private final MetricsLogger mMetricsLogger = new MetricsLogger(); // Cursor Controllers. @@ -1732,6 +1734,9 @@ public class Editor { @VisibleForTesting public void onTouchEvent(MotionEvent event) { final boolean filterOutEvent = shouldFilterOutTouchEvent(event); + + mLastToolType = event.getToolType(event.getActionIndex()); + mLastButtonState = event.getButtonState(); if (filterOutEvent) { if (event.getActionMasked() == MotionEvent.ACTION_UP) { @@ -1784,7 +1789,7 @@ public class Editor { } private void showFloatingToolbar() { - if (mTextActionMode != null) { + if (mTextActionMode != null && showUIForFingerInput()) { // Delay "show" so it doesn't interfere with click confirmations // or double-clicks that could "dismiss" the floating toolbar. int delay = ViewConfiguration.getDoubleTapTimeout(); @@ -1864,7 +1869,8 @@ public class Editor { final CursorController cursorController = mTextView.hasSelection() ? getSelectionController() : getInsertionController(); if (cursorController != null && !cursorController.isActive() - && !cursorController.isCursorBeingModified()) { + && !cursorController.isCursorBeingModified() + && showUIForFingerInput()) { cursorController.show(); } } @@ -2515,6 +2521,10 @@ public class Editor { return false; } + if (!showUIForFingerInput()) { + return false; + } + ActionMode.Callback actionModeCallback = new TextActionModeCallback(actionMode); mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING); registerOnBackInvokedCallback(); @@ -2667,7 +2677,7 @@ public class Editor { mTextView.postDelayed(mShowSuggestionRunnable, ViewConfiguration.getDoubleTapTimeout()); } else if (hasInsertionController()) { - if (shouldInsertCursor) { + if (shouldInsertCursor && showUIForFingerInput()) { getInsertionController().show(); } else { getInsertionController().hide(); @@ -5397,7 +5407,8 @@ public class Editor { final PointF showPosInView = new PointF(); final boolean shouldShow = checkForTransforms() /*check not rotated and compute scale*/ && !tooLargeTextForMagnifier() - && obtainMagnifierShowCoordinates(event, showPosInView); + && obtainMagnifierShowCoordinates(event, showPosInView) + && showUIForFingerInput(); if (shouldShow) { // Make the cursor visible and stop blinking. mRenderCursorRegardlessTiming = true; @@ -6343,6 +6354,15 @@ public class Editor { } } + /** + * Returns true when need to show UIs, e.g. floating toolbar, etc, for finger based interaction. + * + * @return true if UIs need to show for finger interaciton. false if UIs are not necessary. + */ + public boolean showUIForFingerInput() { + return mLastToolType != MotionEvent.TOOL_TYPE_MOUSE; + } + /** Controller for the insertion cursor. */ @VisibleForTesting public class InsertionPointCursorController implements CursorController { diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index a0ec48bc6beb..54a415cfa01b 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -301,7 +301,11 @@ public final class SelectionActionModeHelper { final SelectionModifierCursorController controller = mEditor.getSelectionController(); if (controller != null && (mTextView.isTextSelectable() || mTextView.isTextEditable())) { - controller.show(); + if (mEditor.showUIForFingerInput()) { + controller.show(); + } else { + controller.hide(); + } } if (result != null) { switch (actionMode) { diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java index 2bef10f1aee5..b63ce1b22cdb 100644 --- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java +++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java @@ -968,27 +968,6 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub }); } - /** - * Dispatches {@link InputConnection#requestCursorUpdates(int)}. - * - * <p>This method is intended to be called only from {@link InputMethodManager}.</p> - * @param cursorUpdateMode the mode for {@link InputConnection#requestCursorUpdates(int, int)} - * @param cursorUpdateFilter the filter for - * {@link InputConnection#requestCursorUpdates(int, int)} - * @param imeDisplayId displayId on which IME is displayed. - */ - @Dispatching(cancellable = true) - public void requestCursorUpdatesFromImm(int cursorUpdateMode, int cursorUpdateFilter, - int imeDisplayId) { - final int currentSessionId = mCurrentSessionId.get(); - dispatchWithTracing("requestCursorUpdatesFromImm", () -> { - if (currentSessionId != mCurrentSessionId.get()) { - return; // cancelled - } - requestCursorUpdatesInternal(cursorUpdateMode, cursorUpdateFilter, imeDisplayId); - }); - } - @Dispatching(cancellable = true) @Override public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode, diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index 5e34c15c42e2..134a91710c0b 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -137,4 +137,13 @@ public class DecorContext extends ContextThemeWrapper { } return false; } + + @Override + public boolean isConfigurationContext() { + Context context = mContext.get(); + if (context != null) { + return context.isConfigurationContext(); + } + return false; + } } diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index b9243ece9158..9474f6fc3252 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,6 +1,7 @@ package com.android.internal.util; import static android.content.Intent.ACTION_USER_SWITCHED; +import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,7 +28,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; -import android.view.WindowManager; import android.view.WindowManager.ScreenshotSource; import android.view.WindowManager.ScreenshotType; @@ -42,10 +42,15 @@ public class ScreenshotHelper { public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2; /** - * Describes a screenshot request (to make it easier to pass data through to the handler). + * Describes a screenshot request. */ public static class ScreenshotRequest implements Parcelable { + @ScreenshotType + private final int mType; + + @ScreenshotSource private final int mSource; + private final Bundle mBitmapBundle; private final Rect mBoundsInScreen; private final Insets mInsets; @@ -53,20 +58,27 @@ public class ScreenshotHelper { private final int mUserId; private final ComponentName mTopComponent; - @VisibleForTesting - public ScreenshotRequest(int source) { - mSource = source; - mBitmapBundle = null; - mBoundsInScreen = null; - mInsets = null; - mTaskId = -1; - mUserId = -1; - mTopComponent = null; + + public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source) { + this(type, source, /* topComponent */ null); } - @VisibleForTesting - public ScreenshotRequest(int source, Bundle bitmapBundle, Rect boundsInScreen, - Insets insets, int taskId, int userId, ComponentName topComponent) { + public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source, + ComponentName topComponent) { + this(type, + source, + /* bitmapBundle*/ null, + /* boundsInScreen */ null, + /* insets */ null, + /* taskId */ -1, + /* userId */ -1, + topComponent); + } + + public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source, + Bundle bitmapBundle, Rect boundsInScreen, Insets insets, int taskId, int userId, + ComponentName topComponent) { + mType = type; mSource = source; mBitmapBundle = bitmapBundle; mBoundsInScreen = boundsInScreen; @@ -77,6 +89,7 @@ public class ScreenshotHelper { } ScreenshotRequest(Parcel in) { + mType = in.readInt(); mSource = in.readInt(); if (in.readInt() == 1) { mBitmapBundle = in.readBundle(getClass().getClassLoader()); @@ -96,6 +109,12 @@ public class ScreenshotHelper { } } + @ScreenshotType + public int getType() { + return mType; + } + + @ScreenshotSource public int getSource() { return mSource; } @@ -131,6 +150,7 @@ public class ScreenshotHelper { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); dest.writeInt(mSource); if (mBitmapBundle == null) { dest.writeInt(0); @@ -208,8 +228,7 @@ public class ScreenshotHelper { * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .} * * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing - * this - * Bitmap on to any other source. + * this Bitmap on to any other source. * * @param bundle containing the bitmap * @return a hardware Bitmap @@ -261,16 +280,16 @@ public class ScreenshotHelper { * Added to support reducing unit test duration; the method variant without a timeout argument * is recommended for general use. * - * @param screenshotType The type of screenshot, defined by {@link ScreenshotType} + * @param type The type of screenshot, defined by {@link ScreenshotType} * @param source The source of the screenshot request, defined by {@link ScreenshotSource} * @param handler used to process messages received from the screenshot service * @param completionConsumer receives the URI of the captured screenshot, once saved or * null if no screenshot was saved */ - public void takeScreenshot(@ScreenshotType int screenshotType, @ScreenshotSource int source, + public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source, @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(source); - takeScreenshot(screenshotType, handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, + ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source); + takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, completionConsumer); } @@ -280,7 +299,7 @@ public class ScreenshotHelper { * Added to support reducing unit test duration; the method variant without a timeout argument * is recommended for general use. * - * @param screenshotType The type of screenshot, defined by {@link ScreenshotType} + * @param type The type of screenshot, defined by {@link ScreenshotType} * @param source The source of the screenshot request, defined by {@link ScreenshotSource} * @param handler used to process messages received from the screenshot service * @param timeoutMs time limit for processing, intended only for testing @@ -288,10 +307,10 @@ public class ScreenshotHelper { * null if no screenshot was saved */ @VisibleForTesting - public void takeScreenshot(@ScreenshotType int screenshotType, @ScreenshotSource int source, + public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source, @NonNull Handler handler, long timeoutMs, @Nullable Consumer<Uri> completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(source); - takeScreenshot(screenshotType, handler, screenshotRequest, timeoutMs, completionConsumer); + ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source); + takeScreenshot(handler, screenshotRequest, timeoutMs, completionConsumer); } /** @@ -312,14 +331,12 @@ public class ScreenshotHelper { @NonNull Insets insets, int taskId, int userId, ComponentName topComponent, @ScreenshotSource int source, @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, screenshotBundle, - boundsInScreen, insets, taskId, userId, topComponent); - takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, handler, screenshotRequest, - SCREENSHOT_TIMEOUT_MS, - completionConsumer); + ScreenshotRequest screenshotRequest = new ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, + source, screenshotBundle, boundsInScreen, insets, taskId, userId, topComponent); + takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, completionConsumer); } - private void takeScreenshot(@ScreenshotType int screenshotType, @NonNull Handler handler, + private void takeScreenshot(@NonNull Handler handler, ScreenshotRequest screenshotRequest, long timeoutMs, @Nullable Consumer<Uri> completionConsumer) { synchronized (mScreenshotLock) { @@ -337,7 +354,7 @@ public class ScreenshotHelper { } }; - Message msg = Message.obtain(null, screenshotType, screenshotRequest); + Message msg = Message.obtain(null, 0, screenshotRequest); Handler h = new Handler(handler.getLooper()) { @Override diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml index 58b8cc9438ae..96860b050393 100644 --- a/core/res/res/layout/side_fps_toast.xml +++ b/core/res/res/layout/side_fps_toast.xml @@ -20,13 +20,14 @@ android:layout_height="wrap_content" android:minWidth="350dp" android:layout_gravity="center" - android:theme="?attr/alertDialogTheme"> + android:background="@color/side_fps_toast_background"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/fp_power_button_enrollment_title" android:singleLine="true" android:ellipsize="end" + android:textColor="@color/side_fps_text_color" android:paddingLeft="20dp"/> <Space android:layout_width="wrap_content" @@ -39,5 +40,6 @@ android:text="@string/fp_power_button_enrollment_button_text" android:paddingRight="20dp" style="?android:attr/buttonBarNegativeButtonStyle" + android:textColor="@color/side_fps_button_color" android:maxLines="1"/> </LinearLayout>
\ No newline at end of file diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index e2db49cfa657..88171ce368f9 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -41,4 +41,9 @@ <!-- Lily Language Picker language item view colors --> <color name="language_picker_item_text_color">#F1F3F4</color> <color name="language_picker_item_text_color_secondary">#BDC1C6</color> + + <!-- Color for side fps toast dark theme--> + <color name="side_fps_toast_background">#2E3132</color> + <color name="side_fps_text_color">#EFF1F2</color> + <color name="side_fps_button_color">#33B9DB</color> </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index faf48f3c3fdc..ac083277b787 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -454,4 +454,9 @@ <!-- Lily Language Picker language item view colors --> <color name="language_picker_item_text_color">#202124</color> <color name="language_picker_item_text_color_secondary">#5F6368</color> + + <!-- Color for side fps toast light theme --> + <color name="side_fps_toast_background">#F7F9FA</color> + <color name="side_fps_text_color">#191C1D</color> + <color name="side_fps_button_color">#00677E</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 215376aab11c..9faf5e85c31f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2004,6 +2004,9 @@ on grouped devices. --> <bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool> + <!-- Flag indicating current media Output Switcher version. --> + <integer name="config_mediaOutputSwitchDialogVersion">1</integer> + <!-- Flag indicating that an outbound call must have a call capable phone account that has declared it can process the call's handle. --> <bool name="config_requireCallCapableAccountForHandle">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c45db7e7468f..51712ff5e35f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4709,6 +4709,8 @@ <java-symbol type="bool" name="config_volumeAdjustmentForRemoteGroupSessions" /> + <java-symbol type="integer" name="config_mediaOutputSwitchDialogVersion" /> + <!-- List of shared library packages that should be loaded by the classloader after the code and resources provided by applications. --> <java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" /> diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 2054b4fe9a35..8cf118c4b79a 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -18,8 +18,10 @@ package android.view; import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_USER; +import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.statusBars; import static junit.framework.Assert.assertEquals; @@ -28,6 +30,7 @@ import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -75,6 +78,7 @@ public class InsetsSourceConsumerTest { private boolean mRemoveSurfaceCalled = false; private InsetsController mController; private InsetsState mState; + private ViewRootImpl mViewRoot; @Before public void setup() { @@ -86,10 +90,9 @@ public class InsetsSourceConsumerTest { instrumentation.runOnMainSync(() -> { final Context context = instrumentation.getTargetContext(); // cannot mock ViewRootImpl since it's final. - final ViewRootImpl viewRootImpl = new ViewRootImpl(context, - context.getDisplayNoVerify()); + mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify()); try { - viewRootImpl.setView(new TextView(context), new LayoutParams(), null); + mViewRoot.setView(new TextView(context), new LayoutParams(), null); } catch (BadTokenException e) { // activity isn't running, lets ignore BadTokenException. } @@ -97,7 +100,7 @@ public class InsetsSourceConsumerTest { mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); mState.addSource(mSpyInsetsSource); - mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); + mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot)); mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, () -> mMockTransaction, mController) { @Override @@ -207,4 +210,40 @@ public class InsetsSourceConsumerTest { }); } + + @Test + public void testWontUpdateImeLeashVisibility_whenAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + InsetsState state = new InsetsState(); + ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot); + InsetsController insetsController = new InsetsController(host, (controller, type) -> { + if (type == ITYPE_IME) { + return new InsetsSourceConsumer(ITYPE_IME, state, + () -> mMockTransaction, controller) { + @Override + public int requestShow(boolean fromController) { + return SHOW_IMMEDIATELY; + } + }; + } + return new InsetsSourceConsumer(type, controller.getState(), Transaction::new, + controller); + }, host.getHandler()); + InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ITYPE_IME); + + // Initial IME insets source control with its leash. + imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash, + false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + reset(mMockTransaction); + + // Verify when the app requests controlling show IME animation, the IME leash + // visibility won't be updated when the consumer received the same leash in setControl. + insetsController.controlWindowInsetsAnimation(ime(), 0L, + null /* interpolator */, null /* cancellationSignal */, null /* listener */); + assertTrue(insetsController.getAnimationType(ITYPE_IME) == ANIMATION_TYPE_USER); + imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash, + true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + verify(mMockTransaction, never()).show(mLeash); + }); + } } diff --git a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml index 7475abac4695..7475abac4695 100644 --- a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml +++ b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml index ce8640df0093..67467bbc72ae 100644 --- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml +++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml @@ -15,5 +15,5 @@ ~ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@color/tv_pip_menu_icon_unfocused" /> + <item android:color="@color/tv_window_menu_icon_unfocused" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml index 4f5e63dac5c0..4182bfeefa1b 100644 --- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml +++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ 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. @@ -16,6 +16,6 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:color="@color/tv_pip_menu_icon_bg_focused" /> - <item android:color="@color/tv_pip_menu_icon_bg_unfocused" /> + android:color="@color/tv_window_menu_close_icon_bg_focused" /> + <item android:color="@color/tv_window_menu_close_icon_bg_unfocused" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml index 275870450493..45205d2a7138 100644 --- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml +++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ 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. @@ -16,8 +16,8 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:color="@color/tv_pip_menu_icon_focused" /> + android:color="@color/tv_window_menu_icon_focused" /> <item android:state_enabled="false" - android:color="@color/tv_pip_menu_icon_disabled" /> - <item android:color="@color/tv_pip_menu_icon_unfocused" /> + android:color="@color/tv_window_menu_icon_disabled" /> + <item android:color="@color/tv_window_menu_icon_unfocused" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml index 6cbf66f00df7..1bd26e1d6583 100644 --- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml +++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml @@ -16,6 +16,6 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:color="@color/tv_pip_menu_close_icon_bg_focused" /> - <item android:color="@color/tv_pip_menu_close_icon_bg_unfocused" /> + android:color="@color/tv_window_menu_icon_bg_focused" /> + <item android:color="@color/tv_window_menu_icon_bg_unfocused" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml new file mode 100644 index 000000000000..0bcaa530dc80 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml @@ -0,0 +1,24 @@ +<!-- + ~ 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" android:pathData="M6,21V19H18V21Z"/> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml deleted file mode 100644 index 1938f4562e97..000000000000 --- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <corners android:radius="@dimen/pip_menu_button_radius" /> - <solid android:color="@color/tv_pip_menu_icon_bg" /> -</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml index 846fdb3e8a58..7085a2c72c86 100644 --- a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml +++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@integer/pip_menu_fade_animation_duration"> + android:exitFadeDuration="@integer/tv_window_menu_fade_animation_duration"> <item android:state_activated="true"> <shape android:shape="rectangle"> <corners android:radius="@dimen/pip_menu_border_corner_radius" /> diff --git a/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml new file mode 100644 index 000000000000..2dba37daf059 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml @@ -0,0 +1,21 @@ +<?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 xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/tv_window_menu_button_radius" /> + <solid android:color="@color/tv_window_menu_icon_bg" /> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml index a112f1933dd1..d183e42c173b 100644 --- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml +++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml @@ -22,6 +22,17 @@ android:gravity="end" android:background="@drawable/decor_caption_title"> <Button + android:id="@+id/minimize_window" + android:visibility="gone" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_margin="5dp" + android:padding="4dp" + android:layout_gravity="top|end" + android:contentDescription="@string/maximize_button_text" + android:background="@drawable/decor_minimize_button_dark" + android:duplicateParentState="true"/> + <Button android:id="@+id/maximize_window" android:layout_width="32dp" android:layout_height="32dp" @@ -42,4 +53,3 @@ android:background="@drawable/decor_close_button_dark" android:duplicateParentState="true"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout> - diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml index 2d50d3f1392d..8533a5994d33 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml @@ -64,14 +64,14 @@ android:layout_width="@dimen/pip_menu_button_wrapper_margin" android:layout_height="@dimen/pip_menu_button_wrapper_margin"/> - <com.android.wm.shell.pip.tv.TvPipMenuActionButton + <com.android.wm.shell.common.TvWindowMenuActionButton android:id="@+id/tv_pip_menu_fullscreen_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/pip_ic_fullscreen_white" android:text="@string/pip_fullscreen" /> - <com.android.wm.shell.pip.tv.TvPipMenuActionButton + <com.android.wm.shell.common.TvWindowMenuActionButton android:id="@+id/tv_pip_menu_close_button" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -80,14 +80,14 @@ <!-- More TvPipMenuActionButtons may be added here at runtime. --> - <com.android.wm.shell.pip.tv.TvPipMenuActionButton + <com.android.wm.shell.common.TvWindowMenuActionButton android:id="@+id/tv_pip_menu_move_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/pip_ic_move_white" android:text="@string/pip_move" /> - <com.android.wm.shell.pip.tv.TvPipMenuActionButton + <com.android.wm.shell.common.TvWindowMenuActionButton android:id="@+id/tv_pip_menu_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -145,7 +145,7 @@ android:layout_margin="@dimen/pip_menu_outer_space_frame" android:background="@drawable/tv_pip_menu_border"/> - <com.android.wm.shell.pip.tv.TvPipMenuActionButton + <com.android.wm.shell.common.TvWindowMenuActionButton android:id="@+id/tv_pip_menu_done_button" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml deleted file mode 100644 index db96d8de4094..000000000000 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<!-- Layout for TvPipMenuActionButton --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/button" - android:layout_width="@dimen/pip_menu_button_size" - android:layout_height="@dimen/pip_menu_button_size" - android:padding="@dimen/pip_menu_button_margin" - android:stateListAnimator="@animator/tv_pip_menu_action_button_animator" - android:focusable="true"> - - <View android:id="@+id/background" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:duplicateParentState="true" - android:background="@drawable/tv_pip_button_bg"/> - - <ImageView android:id="@+id/icon" - android:layout_width="@dimen/pip_menu_icon_size" - android:layout_height="@dimen/pip_menu_icon_size" - android:layout_gravity="center" - android:duplicateParentState="true" - android:tint="@color/tv_pip_menu_icon" /> -</FrameLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml new file mode 100644 index 000000000000..c4dbd39c729a --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml @@ -0,0 +1,40 @@ +<?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. + --> +<!-- Layout for TvWindowMenuActionButton --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/button" + android:layout_width="@dimen/tv_window_menu_button_size" + android:layout_height="@dimen/tv_window_menu_button_size" + android:padding="@dimen/tv_window_menu_button_margin" + android:stateListAnimator="@animator/tv_window_menu_action_button_animator" + android:focusable="true"> + + <View android:id="@+id/background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:duplicateParentState="true" + android:background="@drawable/tv_window_button_bg"/> + + <ImageView android:id="@+id/icon" + android:layout_width="@dimen/tv_window_menu_icon_size" + android:layout_height="@dimen/tv_window_menu_icon_size" + android:layout_gravity="center" + android:duplicateParentState="true" + android:tint="@color/tv_window_menu_icon" /> +</FrameLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml index 7993e03b2464..75db421ec405 100644 --- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml @@ -23,7 +23,7 @@ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string> <string name="pip_move" msgid="158770205886688553">"Mover"</string> <string name="pip_expand" msgid="1051966011679297308">"Mostrar"</string> - <string name="pip_collapse" msgid="3903295106641385962">"Ocultar"</string> + <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string> <string name="pip_edu_text" msgid="3672999496647508701">" Pulsa dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string> <string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de imagen en imagen."</string> <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string> diff --git a/libs/WindowManager/Shell/res/values-television/dimen.xml b/libs/WindowManager/Shell/res/values-television/dimen.xml index 14e89f8b08df..376cc4faca6f 100644 --- a/libs/WindowManager/Shell/res/values-television/dimen.xml +++ b/libs/WindowManager/Shell/res/values-television/dimen.xml @@ -21,4 +21,7 @@ <!-- Padding between PIP and keep clear areas that caused it to move. --> <dimen name="pip_keep_clear_area_padding">16dp</dimen> + + <!-- The corner radius for PiP window. --> + <dimen name="pip_corner_radius">0dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml index 02e726fbc3bf..b45b9ec0c457 100644 --- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml +++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml @@ -15,20 +15,20 @@ limitations under the License. --> <resources> - <!-- The dimensions to user for picture-in-picture action buttons. --> - <dimen name="pip_menu_button_size">48dp</dimen> - <dimen name="pip_menu_button_radius">20dp</dimen> - <dimen name="pip_menu_icon_size">20dp</dimen> - <dimen name="pip_menu_button_margin">4dp</dimen> - <dimen name="pip_menu_button_wrapper_margin">26dp</dimen> - <dimen name="pip_menu_border_width">4dp</dimen> - <integer name="pip_menu_fade_animation_duration">500</integer> + <!-- The dimensions to use for tv window menu action buttons. --> + <dimen name="tv_window_menu_button_size">48dp</dimen> + <dimen name="tv_window_menu_button_radius">20dp</dimen> + <dimen name="tv_window_menu_icon_size">20dp</dimen> + <dimen name="tv_window_menu_button_margin">4dp</dimen> + <integer name="tv_window_menu_fade_animation_duration">500</integer> <!-- The pip menu front border corner radius is 2dp smaller than the background corner radius to hide the background from showing through. --> <dimen name="pip_menu_border_corner_radius">4dp</dimen> <dimen name="pip_menu_background_corner_radius">6dp</dimen> + <dimen name="pip_menu_border_width">4dp</dimen> <dimen name="pip_menu_outer_space">24dp</dimen> + <dimen name="pip_menu_button_wrapper_margin">26dp</dimen> <!-- outer space minus border width --> <dimen name="pip_menu_outer_space_frame">20dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml index fa90fe36b545..3e71c1010278 100644 --- a/libs/WindowManager/Shell/res/values/colors_tv.xml +++ b/libs/WindowManager/Shell/res/values/colors_tv.xml @@ -15,13 +15,15 @@ ~ limitations under the License. --> <resources> - <color name="tv_pip_menu_icon_focused">#0E0E0F</color> - <color name="tv_pip_menu_icon_unfocused">#F8F9FA</color> - <color name="tv_pip_menu_icon_disabled">#80868B</color> - <color name="tv_pip_menu_close_icon_bg_focused">#D93025</color> - <color name="tv_pip_menu_close_icon_bg_unfocused">#D69F261F</color> - <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color> - <color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color> + <color name="tv_window_menu_icon_focused">#0E0E0F</color> + <color name="tv_window_menu_icon_unfocused">#F8F9FA</color> + + <color name="tv_window_menu_icon_disabled">#80868B</color> + <color name="tv_window_menu_close_icon_bg_focused">#D93025</color> + <color name="tv_window_menu_close_icon_bg_unfocused">#D69F261F</color> + <color name="tv_window_menu_icon_bg_focused">#E8EAED</color> + <color name="tv_window_menu_icon_bg_unfocused">#990E0E0F</color> + <color name="tv_pip_menu_focus_border">#E8EAED</color> <color name="tv_pip_menu_background">#1E232C</color> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 2a38766f409e..679bfb9d5187 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -197,6 +197,8 @@ <!-- Freeform window caption strings --> <!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] --> <string name="maximize_button_text">Maximize</string> + <!-- Accessibility text for the minimize window button [CHAR LIMIT=NONE] --> + <string name="minimize_button_text">Minimize</string> <!-- Accessibility text for the close window button [CHAR LIMIT=NONE] --> <string name="close_button_text">Close</string> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index d28a68a42b2b..a8764e05c3e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -54,7 +54,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, /** Callback for listening task state. */ public interface Listener { - /** Called when the container is ready for launching activities. */ + /** + * Only called once when the surface has been created & the container is ready for + * launching activities. + */ default void onInitialized() {} /** Called when the container can no longer launch activities. */ @@ -80,12 +83,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final SyncTransactionQueue mSyncQueue; private final TaskViewTransitions mTaskViewTransitions; - private ActivityManager.RunningTaskInfo mTaskInfo; + protected ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; private SurfaceControl mTaskLeash; private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); private boolean mSurfaceCreated; private boolean mIsInitialized; + private boolean mNotifiedForInitialized; private Listener mListener; private Executor mListenerExecutor; private Region mObscuredTouchRegion; @@ -110,6 +114,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mGuard.open("release"); } + /** + * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise. + */ + public boolean isInitialized() { + return mIsInitialized; + } + /** Until all users are converted, we may have mixed-use (eg. Car). */ private boolean isUsingShellTransitions() { return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS; @@ -269,11 +280,17 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, resetTaskInfo(); }); mGuard.close(); - if (mListener != null && mIsInitialized) { + mIsInitialized = false; + notifyReleased(); + } + + /** Called when the {@link TaskView} has been released. */ + protected void notifyReleased() { + if (mListener != null && mNotifiedForInitialized) { mListenerExecutor.execute(() -> { mListener.onReleased(); }); - mIsInitialized = false; + mNotifiedForInitialized = false; } } @@ -407,12 +424,8 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void surfaceCreated(SurfaceHolder holder) { mSurfaceCreated = true; - if (mListener != null && !mIsInitialized) { - mIsInitialized = true; - mListenerExecutor.execute(() -> { - mListener.onInitialized(); - }); - } + mIsInitialized = true; + notifyInitialized(); mShellExecutor.execute(() -> { if (mTaskToken == null) { // Nothing to update, task is not yet available @@ -430,6 +443,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, }); } + /** Called when the {@link TaskView} is initialized. */ + protected void notifyInitialized() { + if (mListener != null && !mNotifiedForInitialized) { + mNotifiedForInitialized = true; + mListenerExecutor.execute(() -> { + mListener.onInitialized(); + }); + } + } + @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { if (mTaskToken == null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java index a09aab666a31..572e3335eb11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.pip.tv; +package com.android.wm.shell.common; import android.content.Context; import android.content.res.TypedArray; @@ -28,33 +28,32 @@ import android.widget.RelativeLayout; import com.android.wm.shell.R; /** - * A View that represents Pip Menu action button, such as "Fullscreen" and "Close" as well custom - * (provided by the application in Pip) and media buttons. + * A common action button for TV window menu layouts. */ -public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener { +public class TvWindowMenuActionButton extends RelativeLayout implements View.OnClickListener { private final ImageView mIconImageView; private final View mButtonBackgroundView; private final View mButtonView; private OnClickListener mOnClickListener; - public TvPipMenuActionButton(Context context) { + public TvWindowMenuActionButton(Context context) { this(context, null, 0, 0); } - public TvPipMenuActionButton(Context context, AttributeSet attrs) { + public TvWindowMenuActionButton(Context context, AttributeSet attrs) { this(context, attrs, 0, 0); } - public TvPipMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) { + public TvWindowMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public TvPipMenuActionButton( + public TvWindowMenuActionButton( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.tv_pip_menu_action_button, this); + inflater.inflate(R.layout.tv_window_menu_action_button, this); mIconImageView = findViewById(R.id.icon); mButtonView = findViewById(R.id.button); @@ -129,20 +128,27 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic return mButtonView.isEnabled(); } - void setIsCustomCloseAction(boolean isCustomCloseAction) { + /** + * Marks this button as a custom close action button. + * This changes the style of the action button to highlight that this action finishes the + * Picture-in-Picture activity. + * + * @param isCustomCloseAction sets or unsets this button as a custom close action button. + */ + public void setIsCustomCloseAction(boolean isCustomCloseAction) { mIconImageView.setImageTintList( getResources().getColorStateList( - isCustomCloseAction ? R.color.tv_pip_menu_close_icon - : R.color.tv_pip_menu_icon)); + isCustomCloseAction ? R.color.tv_window_menu_close_icon + : R.color.tv_window_menu_icon)); mButtonBackgroundView.setBackgroundTintList(getResources() - .getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg - : R.color.tv_pip_menu_icon_bg)); + .getColorStateList(isCustomCloseAction ? R.color.tv_window_menu_close_icon_bg + : R.color.tv_window_menu_icon_bg)); } @Override public String toString() { if (mButtonView.getContentDescription() == null) { - return TvPipMenuActionButton.class.getSimpleName(); + return TvWindowMenuActionButton.class.getSimpleName(); } return mButtonView.getContentDescription().toString(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index af205ed051d7..a1e9f938d280 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -92,6 +92,12 @@ public class FreeformTaskTransitionHandler } @Override + public void startMinimizedModeTransition(WindowContainerTransaction wct) { + final int type = WindowManager.TRANSIT_TO_BACK; + mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this)); + } + + @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @@ -121,6 +127,8 @@ public class FreeformTaskTransitionHandler transition, info.getType(), change, startT, finishT); break; case WindowManager.TRANSIT_TO_BACK: + transitionHandled |= startMinimizeTransition(transition); + break; case WindowManager.TRANSIT_TO_FRONT: break; } @@ -169,6 +177,13 @@ public class FreeformTaskTransitionHandler return false; } + private boolean startMinimizeTransition(IBinder transition) { + if (!mPendingTransitionTokens.contains(transition)) { + return false; + } + return true; + } + private boolean startChangeTransition( IBinder transition, int type, @@ -243,4 +258,5 @@ public class FreeformTaskTransitionHandler return null; } } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java index 25eaa0e05a09..c947cf1b8cd1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java @@ -29,6 +29,15 @@ public interface FreeformTaskTransitionStarter { * * @param targetWindowingMode the target windowing mode * @param wct the {@link WindowContainerTransaction} that changes the windowing mode + * */ void startWindowingModeTransition(int targetWindowingMode, WindowContainerTransaction wct); -} + + /** + * Starts window minimization transition + * + * @param wct the {@link WindowContainerTransaction} that changes the windowing mode + * + */ + void startMinimizedModeTransition(WindowContainerTransaction wct); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 57d3a44ed2af..4d7c8465bcc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -56,6 +56,7 @@ import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; +import com.android.wm.shell.common.TvWindowMenuActionButton; import com.android.wm.shell.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -79,7 +80,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final LinearLayout mActionButtonsContainer; private final View mMenuFrameView; - private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>(); + private final List<TvWindowMenuActionButton> mAdditionalButtons = new ArrayList<>(); private final View mPipFrameView; private final View mPipView; private final TextView mEduTextView; @@ -94,7 +95,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final ImageView mArrowRight; private final ImageView mArrowDown; private final ImageView mArrowLeft; - private final TvPipMenuActionButton mA11yDoneButton; + private final TvWindowMenuActionButton mA11yDoneButton; private final ScrollView mScrollView; private final HorizontalScrollView mHorizontalScrollView; @@ -104,8 +105,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private boolean mMoveMenuIsVisible; private boolean mButtonMenuIsVisible; - private final TvPipMenuActionButton mExpandButton; - private final TvPipMenuActionButton mCloseButton; + private final TvWindowMenuActionButton mExpandButton; + private final TvWindowMenuActionButton mCloseButton; private boolean mSwitchingOrientation; @@ -166,7 +167,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { mResizeAnimationDuration = context.getResources().getInteger( R.integer.config_pipResizeAnimationDuration); mPipMenuFadeAnimationDuration = context.getResources() - .getInteger(R.integer.pip_menu_fade_animation_duration); + .getInteger(R.integer.tv_window_menu_fade_animation_duration); mPipMenuOuterSpace = context.getResources() .getDimensionPixelSize(R.dimen.pip_menu_outer_space); @@ -568,7 +569,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { if (actionsNumber > buttonsNumber) { // Add buttons until we have enough to display all the actions. while (actionsNumber > buttonsNumber) { - TvPipMenuActionButton button = new TvPipMenuActionButton(mContext); + TvWindowMenuActionButton button = new TvWindowMenuActionButton(mContext); button.setOnClickListener(this); mActionButtonsContainer.addView(button, @@ -591,7 +592,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { // "Assign" actions to the buttons. for (int index = 0; index < actionsNumber; index++) { final RemoteAction action = actions.get(index); - final TvPipMenuActionButton button = mAdditionalButtons.get(index); + final TvWindowMenuActionButton button = mAdditionalButtons.get(index); // Remove action if it matches the custom close action. if (PipUtils.remoteActionsMatch(action, closeAction)) { @@ -607,7 +608,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { } } - private void setActionForButton(RemoteAction action, TvPipMenuActionButton button, + private void setActionForButton(RemoteAction action, TvWindowMenuActionButton button, Handler mainHandler) { button.setVisibility(View.VISIBLE); // Ensure the button is visible. if (action.getContentDescription().length() > 0) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index b70bde3a64ee..7b498e4f54ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -246,7 +246,7 @@ public class TaskSnapshotWindow { window.setOuter(snapshotSurface); try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout"); - session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, + session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0, tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState, tmpControls, new Bundle()); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 279d57a23e6e..9335438cea50 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -119,6 +119,8 @@ public class Transitions implements RemoteCallable<Transitions> { /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + private final ArrayList<TransitionObserver> mObservers = new ArrayList<>(); + /** List of {@link Runnable} instances to run when the last active transition has finished. */ private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>(); @@ -242,6 +244,16 @@ public class Transitions implements RemoteCallable<Transitions> { mRemoteTransitionHandler.removeFiltered(remoteTransition); } + /** Registers an observer on the lifecycle of transitions. */ + public void registerObserver(@NonNull TransitionObserver observer) { + mObservers.add(observer); + } + + /** Unregisters the observer. */ + public void unregisterObserver(@NonNull TransitionObserver observer) { + mObservers.remove(observer); + } + /** Boosts the process priority of remote animation player. */ public static void setRunningRemoteTransitionDelegate(IApplicationThread appThread) { if (appThread == null) return; @@ -407,6 +419,11 @@ public class Transitions implements RemoteCallable<Transitions> { + Arrays.toString(mActiveTransitions.stream().map( activeTransition -> activeTransition.mToken).toArray())); } + + for (int i = 0; i < mObservers.size(); ++i) { + mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT); + } + if (!info.getRootLeash().isValid()) { // Invalid root-leash implies that the transition is empty/no-op, so just do // housekeeping and return. @@ -474,6 +491,10 @@ public class Transitions implements RemoteCallable<Transitions> { } private void playTransition(@NonNull ActiveTransition active) { + for (int i = 0; i < mObservers.size(); ++i) { + mObservers.get(i).onTransitionStarting(active.mToken); + } + setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT); // If a handler already chose to run this animation, try delegating to it first. @@ -546,6 +567,10 @@ public class Transitions implements RemoteCallable<Transitions> { active.mHandler.onTransitionConsumed( active.mToken, abort, abort ? null : active.mFinishT); } + for (int i = 0; i < mObservers.size(); ++i) { + mObservers.get(i).onTransitionMerged( + active.mToken, mActiveTransitions.get(0).mToken); + } return; } final ActiveTransition active = mActiveTransitions.get(activeIdx); @@ -555,6 +580,9 @@ public class Transitions implements RemoteCallable<Transitions> { active.mHandler.onTransitionConsumed( transition, true /* aborted */, null /* finishTransaction */); } + for (int i = 0; i < mObservers.size(); ++i) { + mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted); + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished (abort=%b), notifying core %s", abort, transition); // Merge all relevant transactions together @@ -593,6 +621,9 @@ public class Transitions implements RemoteCallable<Transitions> { transition, true /* aborted */, null /* finishTransaction */); } mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */); + for (int i = 0; i < mObservers.size(); ++i) { + mObservers.get(i).onTransitionFinished(active.mToken, true); + } } if (mActiveTransitions.size() <= activeIdx) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations " @@ -792,6 +823,52 @@ public class Transitions implements RemoteCallable<Transitions> { default void setAnimScaleSetting(float scale) {} } + /** + * Interface for something that needs to know the lifecycle of some transitions, but never + * handles any transition by itself. + */ + public interface TransitionObserver { + /** + * Called when the transition is ready to play. It may later be merged into other + * transitions. Note this doesn't mean this transition will be played anytime soon. + * + * @param transition the unique token of this transition + * @param startTransaction the transaction given to the handler to be applied before the + * transition animation. This will be applied when the transition + * handler that handles this transition starts the transition. + * @param finishTransaction the transaction given to the handler to be applied after the + * transition animation. The Transition system will apply it when + * finishCallback is called by the transition handler. + */ + void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction); + + /** + * Called when the transition is starting to play. It isn't called for merged transitions. + * + * @param transition the unique token of this transition + */ + void onTransitionStarting(@NonNull IBinder transition); + + /** + * Called when a transition is merged into another transition. There won't be any following + * lifecycle calls for the merged transition. + * + * @param merged the unique token of the transition that's merged to another one + * @param playing the unique token of the transition that accepts the merge + */ + void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing); + + /** + * Called when the transition is finished. This isn't called for merged transitions. + * + * @param transition the unique token of this transition + * @param aborted {@code true} if this transition is aborted; {@code false} otherwise. + */ + void onTransitionFinished(@NonNull IBinder transition, boolean aborted); + } + @BinderThread private class TransitionPlayerImpl extends ITransitionPlayer.Stub { @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 08d6c50f94b4..e7695926d244 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -51,7 +51,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption private final Choreographer mMainChoreographer; private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; - private FreeformTaskTransitionStarter mTransitionStarter; public CaptionWindowDecorViewModel( @@ -168,6 +167,14 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption } else { mSyncQueue.queue(wct); } + } else if (id == R.id.minimize_window) { + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reorder(mTaskToken, false); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTransitionStarter.startMinimizedModeTransition(wct); + } else { + mSyncQueue.queue(wct); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index dc212fc2ab4d..98b5ee9f0cfb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -161,12 +161,13 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL */ private void setupRootView() { View caption = mResult.mRootView.findViewById(R.id.caption); - caption.setOnTouchListener(mOnCaptionTouchListener); View maximize = caption.findViewById(R.id.maximize_window); maximize.setOnClickListener(mOnCaptionButtonClickListener); View close = caption.findViewById(R.id.close_window); close.setOnClickListener(mOnCaptionButtonClickListener); + View minimize = caption.findViewById(R.id.minimize_window); + minimize.setOnClickListener(mOnCaptionButtonClickListener); } void setCaptionColor(int captionColor) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index 1c587a2078cf..0a54b8c016fb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -49,7 +49,10 @@ abstract class BaseTest @JvmOverloads constructor( ) { init { testSpec.setIsTablet( - WindowManagerStateHelper(instrumentation).currentState.wmState.isTablet + WindowManagerStateHelper( + instrumentation, + clearCacheAfterParsing = false + ).currentState.wmState.isTablet ) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 1e4d23c924e7..330c9c95e484 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -20,6 +20,7 @@ package com.android.wm.shell.flicker import android.view.Surface import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject import com.android.server.wm.traces.common.IComponentMatcher import com.android.server.wm.traces.common.region.Region @@ -83,23 +84,26 @@ fun FlickerTestParameter.layerIsVisibleAtEnd( } } +fun FlickerTestParameter.layerKeepVisible( + component: IComponentMatcher +) { + assertLayers { + this.isVisible(component) + } +} + fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible( component: IComponentMatcher, splitLeftTop: Boolean ) { assertLayers { - val dividerRegion = this.last().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region this.isInvisible(component) .then() - .invoke("splitAppLayerBoundsBecomesVisible") { - it.visibleRegion(component).coversAtMost( - if (splitLeftTop) { - getSplitLeftTopRegion(dividerRegion, endRotation) - } else { - getSplitRightBottomRegion(dividerRegion, endRotation) - } - ) - } + .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) + .isVisible(component) + .then() + .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) + .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) } } @@ -108,16 +112,7 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible( splitLeftTop: Boolean ) { assertLayers { - val dividerRegion = this.first().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region - this.invoke("splitAppLayerBoundsBecomesVisible") { - it.visibleRegion(component).coversAtMost( - if (splitLeftTop) { - getSplitLeftTopRegion(dividerRegion, endRotation) - } else { - getSplitRightBottomRegion(dividerRegion, endRotation) - } - ) - } + this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) .then() .isVisible(component, true) .then() @@ -141,6 +136,49 @@ fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd( } } +fun FlickerTestParameter.splitAppLayerBoundsKeepVisible( + component: IComponentMatcher, + splitLeftTop: Boolean +) { + assertLayers { + this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) + } +} + +fun FlickerTestParameter.splitAppLayerBoundsChanges( + component: IComponentMatcher, + splitLeftTop: Boolean +) { + assertLayers { + if (splitLeftTop) { + this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) + .then() + .isInvisible(component) + .then() + .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) + } else { + this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation) + } + } +} + +fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider( + component: IComponentMatcher, + splitLeftTop: Boolean, + rotation: Int +): LayersTraceSubject { + return invoke("splitAppLayerBoundsSnapToDivider") { + val dividerRegion = it.layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region + it.visibleRegion(component).coversAtMost( + if (splitLeftTop) { + getSplitLeftTopRegion(dividerRegion, rotation) + } else { + getSplitRightBottomRegion(dividerRegion, rotation) + } + ) + } +} + fun FlickerTestParameter.appWindowBecomesVisible( component: IComponentMatcher ) { @@ -169,6 +207,14 @@ fun FlickerTestParameter.appWindowIsVisibleAtEnd( } } +fun FlickerTestParameter.appWindowKeepVisible( + component: IComponentMatcher +) { + assertWm { + this.isAppWindowVisible(component) + } +} + fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 3708e5f65485..8b717a0cb75e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -23,3 +23,4 @@ const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentMatcher("", "AppPairSplitDivider#") val DOCKED_STACK_DIVIDER_COMPONENT = ComponentMatcher("", "DockedStackDivider#") val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentMatcher("", "StageCoordinatorSplitDivider#") +val SPLIT_DECOR_MANAGER = ComponentMatcher("", "SplitDecorManager#") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index 4877442bacf7..a1226e682e05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -30,6 +30,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.traces.common.IComponentMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import com.android.wm.shell.flicker.SPLIT_DECOR_MANAGER import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME import com.android.wm.shell.flicker.testapp.Components @@ -46,6 +47,7 @@ class SplitScreenHelper( const val NOTIFICATION_SCROLLER = "notification_stack_scroller" const val DIVIDER_BAR = "docked_divider_handle" const val GESTURE_STEP_MS = 16L + const val LONG_PRESS_TIME_MS = 100L private val notificationScrollerSelector: BySelector get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER) @@ -82,6 +84,13 @@ class SplitScreenHelper( Components.SendNotificationActivity.COMPONENT.toFlickerComponent() ) + fun getIme(instrumentation: Instrumentation): SplitScreenHelper = + SplitScreenHelper( + instrumentation, + Components.ImeActivity.LABEL, + Components.ImeActivity.COMPONENT.toFlickerComponent() + ) + fun waitForSplitComplete( wmHelper: WindowManagerStateHelper, primaryApp: IComponentMatcher, @@ -206,6 +215,16 @@ class SplitScreenHelper( } } + fun longPress( + instrumentation: Instrumentation, + point: Point + ) { + val downTime = SystemClock.uptimeMillis() + touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point) + SystemClock.sleep(LONG_PRESS_TIME_MS) + touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point) + } + fun createShortcutOnHotseatIfNotExist( tapl: LauncherInstrumentation, appName: String @@ -220,6 +239,23 @@ class SplitScreenHelper( } } + fun dragDividerToResizeAndWait( + device: UiDevice, + wmHelper: WindowManagerStateHelper + ) { + val displayBounds = wmHelper.currentState.layerState + .displays.firstOrNull { !it.isVirtual } + ?.layerStackSpace + ?: error("Display not found") + val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS) + dividerBar.drag(Point(displayBounds.width * 2 / 3, displayBounds.height * 2 / 3)) + + wmHelper.StateSyncBuilder() + .withAppTransitionIdle() + .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER) + .waitForAndVerify() + } + fun dragDividerToDismissSplit( device: UiDevice, wmHelper: WindowManagerStateHelper @@ -240,5 +276,33 @@ class SplitScreenHelper( SystemClock.sleep(interval.toLong()) dividerBar.click() } + + fun copyContentFromLeftToRight( + instrumentation: Instrumentation, + device: UiDevice, + sourceApp: IComponentMatcher, + destinationApp: IComponentMatcher, + ) { + // Copy text from sourceApp + val textView = device.wait(Until.findObject( + By.res(sourceApp.packageNames.firstOrNull(), "SplitScreenTest")), TIMEOUT_MS) + longPress(instrumentation, textView.getVisibleCenter()) + + val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS) + copyBtn.click() + + // Paste text to destinationApp + val editText = device.wait(Until.findObject( + By.res(destinationApp.packageNames.firstOrNull(), "plain_text_input")), TIMEOUT_MS) + longPress(instrumentation, editText.getVisibleCenter()) + + val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS) + pasteBtn.click() + + // Verify text + if (!textView.getText().contentEquals(editText.getText())) { + error("Fail to copy content in split") + } + } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt new file mode 100644 index 000000000000..f69107eae638 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT +import com.android.wm.shell.flicker.appWindowKeepVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.layerKeepVisible +import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test copy content from the left to the right side of the split-screen. + * + * To run this test: `atest WMShellFlickerTests:CopyContentInSplit` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) { + protected val textEditApp = SplitScreenHelper.getIme(instrumentation) + + // TODO(b/231399940): Remove this once we can use recent shortcut to enter split. + @Before + open fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + eachRun { + textEditApp.launchViaIntent(wmHelper) + // TODO(b/231399940): Use recent shortcut to enter split. + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(primaryApp.appName) + .dragToSplitscreen(primaryApp.`package`, textEditApp.`package`) + SplitScreenHelper.waitForSplitComplete(wmHelper, textEditApp, primaryApp) + } + } + transitions { + SplitScreenHelper.copyContentFromLeftToRight( + instrumentation, device, primaryApp, textEditApp) + } + } + + @Presubmit + @Test + fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) + + @Presubmit + @Test + fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp) + + @Presubmit + @Test + fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp) + + @Presubmit + @Test + fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible( + primaryApp, splitLeftTop = true) + + @Presubmit + @Test + fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible( + textEditApp, splitLeftTop = false) + + @Presubmit + @Test + fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp) + + @Presubmit + @Test + fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp) + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun entireScreenCovered() = + super.entireScreenCovered() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerIsVisibleAtStartAndEnd() = + super.navBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerPositionAtStartAndEnd() = + super.navBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = + super.navBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerIsVisibleAtStartAndEnd() = + super.statusBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() = + super.statusBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = + super.statusBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarLayerIsVisibleAtStartAndEnd() = + super.taskBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarWindowIsAlwaysVisible() = + super.taskBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = SplitScreenHelper.TEST_REPETITIONS, + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt new file mode 100644 index 000000000000..0f4d98d69c00 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT +import com.android.wm.shell.flicker.appWindowKeepVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.layerKeepVisible +import com.android.wm.shell.flicker.splitAppLayerBoundsChanges +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test resize split by dragging the divider bar. + * + * To run this test: `atest WMShellFlickerTests:DragDividerToResize` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) { + + // TODO(b/231399940): Remove this once we can use recent shortcut to enter split. + @Before + open fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + eachRun { + tapl.goHome() + primaryApp.launchViaIntent(wmHelper) + // TODO(b/231399940): Use recent shortcut to enter split. + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + transitions { + SplitScreenHelper.dragDividerToResizeAndWait(device, wmHelper) + } + } + + @Presubmit + @Test + fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) + + @Presubmit + @Test + fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppLayerVisibilityChanges() { + testSpec.assertLayers { + this.isVisible(secondaryApp) + .then() + .isInvisible(secondaryApp) + .then() + .isVisible(secondaryApp) + } + } + + @Presubmit + @Test + fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(secondaryApp) + + @Presubmit + @Test + fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges( + primaryApp, splitLeftTop = false) + + @Presubmit + @Test + fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges( + secondaryApp, splitLeftTop = true) + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun entireScreenCovered() = + super.entireScreenCovered() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerIsVisibleAtStartAndEnd() = + super.navBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerPositionAtStartAndEnd() = + super.navBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = + super.navBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerIsVisibleAtStartAndEnd() = + super.statusBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() = + super.statusBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = + super.statusBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarLayerIsVisibleAtStartAndEnd() = + super.taskBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarWindowIsAlwaysVisible() = + super.taskBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = SplitScreenHelper.TEST_REPETITIONS, + supportedRotations = listOf(Surface.ROTATION_0), + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index 38279a3dfd17..bdfd9c7de32f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt @@ -29,6 +29,7 @@ import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.helpers.SplitScreenHelper import com.android.wm.shell.flicker.layerIsVisibleAtEnd +import com.android.wm.shell.flicker.layerKeepVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import org.junit.Assume import org.junit.Before @@ -80,11 +81,7 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen @Presubmit @Test - fun splitScreenDividerKeepVisible() { - testSpec.assertLayers { - this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - } - } + fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt new file mode 100644 index 000000000000..da954d97aec2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.wm.shell.flicker.appWindowBecomesVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.layerBecomesVisible +import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd +import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switch to split pair from another app. + * + * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) { + val thirdApp = SplitScreenHelper.getNonResizeable(instrumentation) + + // TODO(b/231399940): Remove this once we can use recent shortcut to enter split. + @Before + open fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + eachRun { + primaryApp.launchViaIntent(wmHelper) + // TODO(b/231399940): Use recent shortcut to enter split. + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + + thirdApp.launchViaIntent(wmHelper) + wmHelper.StateSyncBuilder() + .withAppTransitionIdle() + .withWindowSurfaceAppeared(thirdApp) + .waitForAndVerify() + } + } + transitions { + tapl.launchedAppState.quickSwitchToPreviousApp() + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + @Presubmit + @Test + fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() + + @Presubmit + @Test + fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp) + + @Presubmit + @Test + fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, splitLeftTop = false) + + @Presubmit + @Test + fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, splitLeftTop = true) + + @Presubmit + @Test + fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun entireScreenCovered() = + super.entireScreenCovered() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerIsVisibleAtStartAndEnd() = + super.navBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerPositionAtStartAndEnd() = + super.navBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = + super.navBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerIsVisibleAtStartAndEnd() = + super.statusBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() = + super.statusBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = + super.statusBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarLayerIsVisibleAtStartAndEnd() = + super.taskBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarWindowIsAlwaysVisible() = + super.taskBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = SplitScreenHelper.TEST_REPETITIONS, + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index c48f3f77858b..db89ff52178b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -26,11 +26,8 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group1 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.wm.shell.flicker.appWindowBecomesVisible -import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.helpers.SplitScreenHelper import com.android.wm.shell.flicker.layerBecomesVisible -import com.android.wm.shell.flicker.layerIsVisibleAtEnd -import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible import org.junit.Assume @@ -42,7 +39,7 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test switch back to split pair after go home + * Test quick switch to split pair from home. * * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome` */ @@ -60,30 +57,30 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas } override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - setup { - eachRun { - primaryApp.launchViaIntent(wmHelper) - // TODO(b/231399940): Use recent shortcut to enter split. - tapl.launchedAppState.taskbar - .openAllApps() - .getAppIcon(secondaryApp.appName) - .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + get() = { + super.transition(this) + setup { + eachRun { + primaryApp.launchViaIntent(wmHelper) + // TODO(b/231399940): Use recent shortcut to enter split. + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + + tapl.goHome() + wmHelper.StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() + } + } + transitions { + tapl.workspace.quickSwitchToPreviousApp() SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - - tapl.goHome() - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .withHomeActivityVisible() - .waitForAndVerify() } } - transitions { - tapl.workspace.quickSwitchToPreviousApp() - SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) - } - } @Presubmit @Test @@ -91,7 +88,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @Presubmit @Test - fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp) + fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) @Presubmit @Test @@ -104,12 +101,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @Presubmit @Test - fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible( + fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( secondaryApp, splitLeftTop = true) @Presubmit @Test - fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp) + fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp) @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt new file mode 100644 index 000000000000..c23cdb610671 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.wm.shell.flicker.appWindowBecomesVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.layerBecomesVisible +import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd +import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test switch back to split pair from recent. + * + * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) { + + // TODO(b/231399940): Remove this once we can use recent shortcut to enter split. + @Before + open fun before() { + Assume.assumeTrue(tapl.isTablet) + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + eachRun { + primaryApp.launchViaIntent(wmHelper) + // TODO(b/231399940): Use recent shortcut to enter split. + tapl.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`) + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + + tapl.goHome() + wmHelper.StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() + } + } + transitions { + tapl.workspace.switchToOverview() + .currentTask + .open() + SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) + } + } + + @Presubmit + @Test + fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() + + @Presubmit + @Test + fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp) + + @Presubmit + @Test + fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, splitLeftTop = false) + + @Presubmit + @Test + fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, splitLeftTop = true) + + @Presubmit + @Test + fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp) + + @Presubmit + @Test + fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun entireScreenCovered() = + super.entireScreenCovered() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerIsVisibleAtStartAndEnd() = + super.navBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarLayerPositionAtStartAndEnd() = + super.navBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = + super.navBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerIsVisibleAtStartAndEnd() = + super.statusBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarLayerPositionAtStartAndEnd() = + super.statusBarLayerPositionAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = + super.statusBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarLayerIsVisibleAtStartAndEnd() = + super.taskBarLayerIsVisibleAtStartAndEnd() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun taskBarWindowIsAlwaysVisible() = + super.taskBarWindowIsAlwaysVisible() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() = + super.visibleLayersShownMoreThanOneConsecutiveEntry() + + /** {@inheritDoc} */ + @Postsubmit + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = SplitScreenHelper.TEST_REPETITIONS, + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml index 84789f5a6c02..642a08b5bbe0 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml @@ -26,6 +26,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center_vertical|center_horizontal" + android:textIsSelectable="true" android:text="PrimaryActivity" android:textAppearance="?android:attr/textAppearanceLarge"/> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java index 32f1587752cb..ff1d2990a82a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java @@ -169,6 +169,7 @@ public class TaskViewTest extends ShellTestCase { mTaskView.onTaskAppeared(mTaskInfo, mLeash); verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any()); + assertThat(mTaskView.isInitialized()).isTrue(); verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean()); } @@ -178,6 +179,7 @@ public class TaskViewTest extends ShellTestCase { mTaskView.surfaceCreated(mock(SurfaceHolder.class)); verify(mViewListener).onInitialized(); + assertThat(mTaskView.isInitialized()).isTrue(); // No task, no visibility change verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean()); } @@ -189,6 +191,7 @@ public class TaskViewTest extends ShellTestCase { mTaskView.surfaceCreated(mock(SurfaceHolder.class)); verify(mViewListener).onInitialized(); + assertThat(mTaskView.isInitialized()).isTrue(); verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true)); } @@ -223,6 +226,7 @@ public class TaskViewTest extends ShellTestCase { verify(mOrganizer).removeListener(eq(mTaskView)); verify(mViewListener).onReleased(); + assertThat(mTaskView.isInitialized()).isFalse(); } @Test @@ -270,6 +274,7 @@ public class TaskViewTest extends ShellTestCase { verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any()); verify(mViewListener, never()).onInitialized(); + assertThat(mTaskView.isInitialized()).isFalse(); // If there's no surface the task should be made invisible verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false)); } @@ -281,6 +286,7 @@ public class TaskViewTest extends ShellTestCase { verify(mTaskViewTransitions, never()).setTaskViewVisible(any(), anyBoolean()); verify(mViewListener).onInitialized(); + assertThat(mTaskView.isInitialized()).isTrue(); // No task, no visibility change verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean()); } @@ -353,6 +359,7 @@ public class TaskViewTest extends ShellTestCase { verify(mOrganizer).removeListener(eq(mTaskView)); verify(mViewListener).onReleased(); + assertThat(mTaskView.isInitialized()).isFalse(); verify(mTaskViewTransitions).removeTaskView(eq(mTaskView)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 388792b63db3..b142039e6aa9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -43,11 +43,13 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -59,8 +61,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; -import android.view.IDisplayWindowListener; -import android.view.IWindowManager; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowManager; @@ -82,6 +82,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.sysui.ShellInit; @@ -89,6 +90,7 @@ import com.android.wm.shell.sysui.ShellInit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import java.util.ArrayList; @@ -688,6 +690,204 @@ public class ShellTransitionTests extends ShellTestCase { verify(runnable4, times(1)).run(); } + @Test + public void testObserverLifecycle_basicTransitionFlow() { + Transitions transitions = createTestTransitions(); + Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); + transitions.registerObserver(observer); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + IBinder transitToken = new Binder(); + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken, info, startT, finishT); + + InOrder observerOrder = inOrder(observer); + observerOrder.verify(observer).onTransitionReady(transitToken, info, startT, finishT); + observerOrder.verify(observer).onTransitionStarting(transitToken); + verify(observer, times(0)).onTransitionFinished(eq(transitToken), anyBoolean()); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(observer).onTransitionFinished(transitToken, false); + } + + @Test + public void testObserverLifecycle_queueing() { + Transitions transitions = createTestTransitions(); + Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); + transitions.registerObserver(observer); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + IBinder transitToken1 = new Binder(); + transitions.requestStartTransition(transitToken1, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); + verify(observer).onTransitionReady(transitToken1, info1, startT1, finishT1); + + IBinder transitToken2 = new Binder(); + transitions.requestStartTransition(transitToken2, + new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); + TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); + verify(observer, times(1)).onTransitionReady(transitToken2, info2, startT2, finishT2); + verify(observer, times(0)).onTransitionStarting(transitToken2); + verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); + verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + // first transition finished + verify(observer, times(1)).onTransitionFinished(transitToken1, false); + verify(observer, times(1)).onTransitionStarting(transitToken2); + verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(observer, times(1)).onTransitionFinished(transitToken2, false); + } + + + @Test + public void testObserverLifecycle_merging() { + Transitions transitions = createTestTransitions(); + Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); + transitions.registerObserver(observer); + mDefaultHandler.setSimulateMerge(true); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + IBinder transitToken1 = new Binder(); + transitions.requestStartTransition(transitToken1, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); + + IBinder transitToken2 = new Binder(); + transitions.requestStartTransition(transitToken2, + new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); + TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); + + InOrder observerOrder = inOrder(observer); + observerOrder.verify(observer).onTransitionReady(transitToken2, info2, startT2, finishT2); + observerOrder.verify(observer).onTransitionMerged(transitToken2, transitToken1); + verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + // transition + merged all finished. + verify(observer, times(1)).onTransitionFinished(transitToken1, false); + // Merged transition won't receive any lifecycle calls beyond ready + verify(observer, times(0)).onTransitionStarting(transitToken2); + verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); + } + + @Test + public void testObserverLifecycle_mergingAfterQueueing() { + Transitions transitions = createTestTransitions(); + Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); + transitions.registerObserver(observer); + mDefaultHandler.setSimulateMerge(true); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + // Make a test handler that only responds to multi-window triggers AND only animates + // Change transitions. + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + TestTransitionHandler testHandler = new TestTransitionHandler() { + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + for (TransitionInfo.Change chg : info.getChanges()) { + if (chg.getMode() == TRANSIT_CHANGE) { + return super.startAnimation(transition, info, startTransaction, + finishTransaction, finishCallback); + } + } + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + final RunningTaskInfo task = request.getTriggerTask(); + return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) + ? handlerWCT : null; + } + }; + transitions.addHandler(testHandler); + + // Use test handler to play an animation + IBinder transitToken1 = new Binder(); + RunningTaskInfo mwTaskInfo = + createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + transitions.requestStartTransition(transitToken1, + new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); + TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) + .addChange(TRANSIT_CHANGE).build(); + SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken1, change, startT1, finishT1); + + // Request the second transition that should be handled by the default handler + IBinder transitToken2 = new Binder(); + TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.requestStartTransition(transitToken2, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken2, open, startT2, finishT2); + verify(observer).onTransitionReady(transitToken2, open, startT2, finishT2); + verify(observer, times(0)).onTransitionStarting(transitToken2); + + // Request the third transition that should be merged into the second one + IBinder transitToken3 = new Binder(); + transitions.requestStartTransition(transitToken3, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + SurfaceControl.Transaction startT3 = mock(SurfaceControl.Transaction.class); + SurfaceControl.Transaction finishT3 = mock(SurfaceControl.Transaction.class); + transitions.onTransitionReady(transitToken3, open, startT3, finishT3); + verify(observer, times(0)).onTransitionStarting(transitToken2); + verify(observer).onTransitionReady(transitToken3, open, startT3, finishT3); + verify(observer, times(0)).onTransitionStarting(transitToken3); + + testHandler.finishAll(); + mMainExecutor.flushAll(); + + verify(observer).onTransitionFinished(transitToken1, false); + + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + + InOrder observerOrder = inOrder(observer); + observerOrder.verify(observer).onTransitionStarting(transitToken2); + observerOrder.verify(observer).onTransitionMerged(transitToken3, transitToken2); + observerOrder.verify(observer).onTransitionFinished(transitToken2, false); + + // Merged transition won't receive any lifecycle calls beyond ready + verify(observer, times(0)).onTransitionStarting(transitToken3); + verify(observer, times(0)).onTransitionFinished(eq(transitToken3), anyBoolean()); + } + class TransitionInfoBuilder { final TransitionInfo mInfo; @@ -834,16 +1034,13 @@ public class ShellTransitionTests extends ShellTestCase { } private DisplayController createTestDisplayController() { - IWindowManager mockWM = mock(IWindowManager.class); - final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1]; - try { - doReturn(new int[]{DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any()); - } catch (RemoteException e) { - // No remote stuff happening, so this can't be hit - } - ShellInit shellInit = new ShellInit(mMainExecutor); - DisplayController out = new DisplayController(mContext, mockWM, shellInit, mMainExecutor); - shellInit.init(); + DisplayLayout displayLayout = mock(DisplayLayout.class); + doReturn(Surface.ROTATION_180).when(displayLayout).getUpsideDownRotation(); + // By default we ignore nav bar in deciding if a seamless rotation is allowed. + doReturn(true).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); + + DisplayController out = mock(DisplayController.class); + doReturn(displayLayout).when(out).getDisplayLayout(DEFAULT_DISPLAY); return out; } @@ -854,17 +1051,4 @@ public class ShellTransitionTests extends ShellTestCase { shellInit.init(); return t; } -// -// private class TestDisplayController extends DisplayController { -// private final DisplayLayout mTestDisplayLayout; -// TestDisplayController() { -// super(mContext, mock(IWindowManager.class), mMainExecutor); -// mTestDisplayLayout = new DisplayLayout(); -// mTestDisplayLayout. -// } -// -// @Override -// DisplayLayout -// } - } diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index 3a8e559f6d7e..687e4dd324d3 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -179,7 +179,7 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) { void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) { int fast_i = (mNumFastRects - 1) * 4; int janky_i = (mNumJankyRects - 1) * 4; - ; + for (size_t fi = 0; fi < mFrameSource.size(); fi++) { if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) { continue; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 2aca41e41905..62e42b8e1863 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -112,7 +112,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { SkCanvas* profileCanvas = surface->getCanvas(); - SkiaProfileRenderer profileRenderer(profileCanvas); + SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height()); profiler->draw(profileRenderer); } diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp index 492c39f1288c..81cfc5d93419 100644 --- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp @@ -33,13 +33,5 @@ void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint } } -uint32_t SkiaProfileRenderer::getViewportWidth() { - return mCanvas->imageInfo().width(); -} - -uint32_t SkiaProfileRenderer::getViewportHeight() { - return mCanvas->imageInfo().height(); -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h index dc8420f4e01b..96d2a5e58139 100644 --- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h +++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h @@ -23,18 +23,21 @@ namespace uirenderer { class SkiaProfileRenderer : public IProfileRenderer { public: - explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {} + explicit SkiaProfileRenderer(SkCanvas* canvas, uint32_t width, uint32_t height) + : mCanvas(canvas), mWidth(width), mHeight(height) {} void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override; void drawRects(const float* rects, int count, const SkPaint& paint) override; - uint32_t getViewportWidth() override; - uint32_t getViewportHeight() override; + uint32_t getViewportWidth() override { return mWidth; } + uint32_t getViewportHeight() override { return mHeight; } virtual ~SkiaProfileRenderer() {} private: // Does not have ownership. SkCanvas* mCanvas; + uint32_t mWidth; + uint32_t mHeight; }; } /* namespace uirenderer */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 905d46e58014..53a4c60cf8a8 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -88,7 +88,9 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { SkCanvas* profileCanvas = backBuffer->getCanvas(); - SkiaProfileRenderer profileRenderer(profileCanvas); + SkAutoCanvasRestore saver(profileCanvas, true); + profileCanvas->concat(mVkSurface->getCurrentPreTransform()); + SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height()); profiler->draw(profileRenderer); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 976117b9bbd4..75d3ff7753cb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -512,9 +512,19 @@ nsecs_t CanvasContext::draw() { ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty)); - const auto drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, - &mLayerUpdateQueue, mContentDrawBounds, mOpaque, - mLightInfo, mRenderNodes, &(profiler())); + IRenderPipeline::DrawResult drawResult; + { + // FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw + // or it can lead to memory corruption. + // This lock is overly broad, but it's the quickest fix since this mutex is otherwise + // not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is + // the thread we're primarily concerned about being responsive, this being too broad + // shouldn't pose a performance issue. + std::scoped_lock lock(mFrameMetricsReporterMutex); + drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, + &mLayerUpdateQueue, mContentDrawBounds, mOpaque, + mLightInfo, mRenderNodes, &(profiler())); + } uint64_t frameCompleteNr = getFrameNumber(); @@ -754,11 +764,11 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId); if (frameInfo != nullptr) { + std::scoped_lock lock(instance->mFrameMetricsReporterMutex); frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime, frameInfo->get(FrameInfoIndex::SwapBuffersCompleted)); frameInfo->set(FrameInfoIndex::GpuCompleted) = std::max( gpuCompleteTime, frameInfo->get(FrameInfoIndex::CommandSubmissionCompleted)); - std::scoped_lock lock(instance->mFrameMetricsReporterMutex); instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber, surfaceControlId); } diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 55ee3aaaaf77..f5a9850b31dd 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -47,6 +47,15 @@ import java.util.StringTokenizer; * <p>All locations generated through {@link LocationManager} are guaranteed to have a valid * latitude, longitude, timestamp (both Unix epoch time and elapsed realtime since boot), and * accuracy. All other parameters are optional. + * + * <p class="note">Note that Android provides the ability for applications to submit "mock" or faked + * locations through {@link LocationManager}, and that these locations can then be received by + * applications using LocationManager to obtain location information. These locations can be + * identified via the {@link #isMock()} API. Applications that wish to determine if a given location + * represents the best estimate of the real position of the device as opposed to a fake location + * coming from another application or the user should use this API. Keep in mind that the user may + * have a good reason for mocking their location, and thus apps should generally reject mock + * locations only when it is essential to their use case that only real locations are accepted. */ public class Location implements Parcelable { diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl index fe15f0e67b1d..29bfd1acae17 100644 --- a/media/java/android/media/IMediaRouter2.aidl +++ b/media/java/android/media/IMediaRouter2.aidl @@ -26,9 +26,7 @@ import android.os.Bundle; oneway interface IMediaRouter2 { void notifyRouterRegistered(in List<MediaRoute2Info> currentRoutes, in RoutingSessionInfo currentSystemSessionInfo); - void notifyRoutesAdded(in List<MediaRoute2Info> routes); - void notifyRoutesRemoved(in List<MediaRoute2Info> routes); - void notifyRoutesChanged(in List<MediaRoute2Info> routes); + void notifyRoutesUpdated(in List<MediaRoute2Info> routes); void notifySessionCreated(int requestId, in @nullable RoutingSessionInfo sessionInfo); void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo); void notifySessionReleased(in RoutingSessionInfo sessionInfo); diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index 71dc2a781ba9..9f3c3ff89032 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -30,8 +30,6 @@ oneway interface IMediaRouter2Manager { void notifySessionReleased(in RoutingSessionInfo session); void notifyDiscoveryPreferenceChanged(String packageName, in RouteDiscoveryPreference discoveryPreference); - void notifyRoutesAdded(in List<MediaRoute2Info> routes); - void notifyRoutesRemoved(in List<MediaRoute2Info> routes); - void notifyRoutesChanged(in List<MediaRoute2Info> routes); + void notifyRoutesUpdated(in List<MediaRoute2Info> routes); void notifyRequestFailed(int requestId, int reason); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index a7a21e7a2013..26cb9f8e9ee1 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -132,7 +132,7 @@ public final class MediaRouter2 { /** * Stores an auxiliary copy of {@link #mFilteredRoutes} at the time of the last route callback * dispatch. This is only used to determine what callback a route should be assigned to (added, - * removed, changed) in {@link #dispatchFilteredRoutesChangedLocked(List)}. + * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}. */ private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>(); @@ -820,7 +820,7 @@ public final class MediaRouter2 { } } - void dispatchFilteredRoutesChangedLocked(List<MediaRoute2Info> newRoutes) { + void dispatchFilteredRoutesUpdatedOnHandler(List<MediaRoute2Info> newRoutes) { List<MediaRoute2Info> addedRoutes = new ArrayList<>(); List<MediaRoute2Info> removedRoutes = new ArrayList<>(); List<MediaRoute2Info> changedRoutes = new ArrayList<>(); @@ -863,29 +863,16 @@ public final class MediaRouter2 { if (!changedRoutes.isEmpty()) { notifyRoutesChanged(changedRoutes); } - } - void addRoutesOnHandler(List<MediaRoute2Info> routes) { - synchronized (mLock) { - for (MediaRoute2Info route : routes) { - mRoutes.put(route.getId(), route); - } - updateFilteredRoutesLocked(); + // Note: We don't notify clients of changes in route ordering. + if (!addedRoutes.isEmpty() || !removedRoutes.isEmpty() || !changedRoutes.isEmpty()) { + notifyRoutesUpdated(newRoutes); } } - void removeRoutesOnHandler(List<MediaRoute2Info> routes) { - synchronized (mLock) { - for (MediaRoute2Info route : routes) { - mRoutes.remove(route.getId()); - } - updateFilteredRoutesLocked(); - } - } - - void changeRoutesOnHandler(List<MediaRoute2Info> routes) { - List<MediaRoute2Info> changedRoutes = new ArrayList<>(); + void updateRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mLock) { + mRoutes.clear(); for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); } @@ -900,8 +887,10 @@ public final class MediaRouter2 { Collections.unmodifiableList( filterRoutesWithCompositePreferenceLocked(List.copyOf(mRoutes.values()))); mHandler.sendMessage( - obtainMessage(MediaRouter2::dispatchFilteredRoutesChangedLocked, - this, mFilteredRoutes)); + obtainMessage( + MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler, + this, + mFilteredRoutes)); } /** @@ -1211,6 +1200,14 @@ public final class MediaRouter2 { } } + private void notifyRoutesUpdated(List<MediaRoute2Info> routes) { + for (RouteCallbackRecord record : mRouteCallbackRecords) { + List<MediaRoute2Info> filteredRoutes = + filterRoutesWithIndividualPreference(routes, record.mPreference); + record.mExecutor.execute(() -> record.mRouteCallback.onRoutesUpdated(filteredRoutes)); + } + } + private void notifyPreferredFeaturesChanged(List<String> features) { for (RouteCallbackRecord record : mRouteCallbackRecords) { record.mExecutor.execute( @@ -1246,29 +1243,44 @@ public final class MediaRouter2 { /** Callback for receiving events about media route discovery. */ public abstract static class RouteCallback { /** - * Called when routes are added. Whenever you registers a callback, this will be invoked - * with known routes. + * Called when routes are added. Whenever you register a callback, this will be invoked with + * known routes. * * @param routes the list of routes that have been added. It's never empty. + * @deprecated Use {@link #onRoutesUpdated(List)} instead. */ + @Deprecated public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {} /** * Called when routes are removed. * * @param routes the list of routes that have been removed. It's never empty. + * @deprecated Use {@link #onRoutesUpdated(List)} instead. */ + @Deprecated public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when routes are changed. For example, it is called when the route's name or volume - * have been changed. + * Called when the properties of one or more existing routes are changed. For example, it is + * called when a route's name or volume have changed. * * @param routes the list of routes that have been changed. It's never empty. + * @deprecated Use {@link #onRoutesUpdated(List)} instead. */ + @Deprecated public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} /** + * Called when the route list is updated, which can happen when routes are added, removed, + * or modified. It will also be called when a route callback is registered. + * + * @param routes the updated list of routes filtered by the callback's individual discovery + * preferences. + */ + public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {} + + /** * Called when the client app's preferred features are changed. When this is called, it is * recommended to {@link #getRoutes()} to get the routes that are currently available to the * app. @@ -1985,21 +1997,9 @@ public final class MediaRouter2 { } @Override - public void notifyRoutesAdded(List<MediaRoute2Info> routes) { - mHandler.sendMessage( - obtainMessage(MediaRouter2::addRoutesOnHandler, MediaRouter2.this, routes)); - } - - @Override - public void notifyRoutesRemoved(List<MediaRoute2Info> routes) { - mHandler.sendMessage( - obtainMessage(MediaRouter2::removeRoutesOnHandler, MediaRouter2.this, routes)); - } - - @Override - public void notifyRoutesChanged(List<MediaRoute2Info> routes) { + public void notifyRoutesUpdated(List<MediaRoute2Info> routes) { mHandler.sendMessage( - obtainMessage(MediaRouter2::changeRoutesOnHandler, MediaRouter2.this, routes)); + obtainMessage(MediaRouter2::updateRoutesOnHandler, MediaRouter2.this, routes)); } @Override @@ -2047,17 +2047,7 @@ public final class MediaRouter2 { class ManagerCallback implements MediaRouter2Manager.Callback { @Override - public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) { - updateAllRoutesFromManager(); - } - - @Override - public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) { - updateAllRoutesFromManager(); - } - - @Override - public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) { + public void onRoutesUpdated() { updateAllRoutesFromManager(); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 44c0b54546be..8afc7d999d2e 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -546,37 +546,15 @@ public final class MediaRouter2Manager { } } - void addRoutesOnHandler(List<MediaRoute2Info> routes) { + void updateRoutesOnHandler(@NonNull List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { + mRoutes.clear(); for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); } } - if (routes.size() > 0) { - notifyRoutesAdded(routes); - } - } - void removeRoutesOnHandler(List<MediaRoute2Info> routes) { - synchronized (mRoutesLock) { - for (MediaRoute2Info route : routes) { - mRoutes.remove(route.getId()); - } - } - if (routes.size() > 0) { - notifyRoutesRemoved(routes); - } - } - - void changeRoutesOnHandler(List<MediaRoute2Info> routes) { - synchronized (mRoutesLock) { - for (MediaRoute2Info route : routes) { - mRoutes.put(route.getId(), route); - } - } - if (routes.size() > 0) { - notifyRoutesChanged(routes); - } + notifyRoutesUpdated(); } void createSessionOnHandler(int requestId, RoutingSessionInfo sessionInfo) { @@ -650,24 +628,9 @@ public final class MediaRouter2Manager { notifySessionUpdated(sessionInfo); } - private void notifyRoutesAdded(List<MediaRoute2Info> routes) { - for (CallbackRecord record: mCallbackRecords) { - record.mExecutor.execute( - () -> record.mCallback.onRoutesAdded(routes)); - } - } - - private void notifyRoutesRemoved(List<MediaRoute2Info> routes) { + private void notifyRoutesUpdated() { for (CallbackRecord record: mCallbackRecords) { - record.mExecutor.execute( - () -> record.mCallback.onRoutesRemoved(routes)); - } - } - - private void notifyRoutesChanged(List<MediaRoute2Info> routes) { - for (CallbackRecord record: mCallbackRecords) { - record.mExecutor.execute( - () -> record.mCallback.onRoutesChanged(routes)); + record.mExecutor.execute(() -> record.mCallback.onRoutesUpdated()); } } @@ -963,23 +926,12 @@ public final class MediaRouter2Manager { * Interface for receiving events about media routing changes. */ public interface Callback { - /** - * Called when routes are added. - * @param routes the list of routes that have been added. It's never empty. - */ - default void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when routes are removed. - * @param routes the list of routes that have been removed. It's never empty. + * Called when the routes list changes. This includes adding, modifying, or removing + * individual routes. */ - default void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {} - - /** - * Called when routes are changed. - * @param routes the list of routes that have been changed. It's never empty. - */ - default void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} + default void onRoutesUpdated() {} /** * Called when a session is changed. @@ -1115,21 +1067,12 @@ public final class MediaRouter2Manager { } @Override - public void notifyRoutesAdded(List<MediaRoute2Info> routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::addRoutesOnHandler, - MediaRouter2Manager.this, routes)); - } - - @Override - public void notifyRoutesRemoved(List<MediaRoute2Info> routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::removeRoutesOnHandler, - MediaRouter2Manager.this, routes)); - } - - @Override - public void notifyRoutesChanged(List<MediaRoute2Info> routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::changeRoutesOnHandler, - MediaRouter2Manager.this, routes)); + public void notifyRoutesUpdated(List<MediaRoute2Info> routes) { + mHandler.sendMessage( + obtainMessage( + MediaRouter2Manager::updateRoutesOnHandler, + MediaRouter2Manager.this, + routes)); } } } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index 4086dec99218..37c836762da0 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -32,7 +32,6 @@ import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_I import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME; import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE; import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME; -import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_NAME2; import static com.android.mediaroutertest.StubMediaRoute2ProviderService.VOLUME_MAX; import static org.junit.Assert.assertEquals; @@ -56,10 +55,10 @@ import android.media.RoutingSessionInfo; import android.os.Bundle; import android.text.TextUtils; -import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.PollingCheck; @@ -69,6 +68,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -115,7 +115,7 @@ public class MediaRouter2ManagerTest { @Before public void setUp() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL, Manifest.permission.MODIFY_AUDIO_ROUTING); @@ -170,51 +170,95 @@ public class MediaRouter2ManagerTest { } @Test - public void testOnRoutesRemovedAndAdded() throws Exception { - RouteCallback routeCallback = new RouteCallback() {}; - mRouteCallbacks.add(routeCallback); - mRouter2.registerRouteCallback(mExecutor, routeCallback, - new RouteDiscoveryPreference.Builder(FEATURES_ALL, true).build()); + public void testOnRoutesUpdated() throws Exception { + final String routeId0 = "routeId0"; + final String routeName0 = "routeName0"; + final String routeId1 = "routeId1"; + final String routeName1 = "routeName1"; + final List<String> features = Collections.singletonList("customFeature"); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); + final int newConnectionState = MediaRoute2Info.CONNECTION_STATE_CONNECTED; + + final List<MediaRoute2Info> routes = new ArrayList<>(); + routes.add(new MediaRoute2Info.Builder(routeId0, routeName0).addFeatures(features).build()); + routes.add(new MediaRoute2Info.Builder(routeId1, routeName1).addFeatures(features).build()); - CountDownLatch removedLatch = new CountDownLatch(1); CountDownLatch addedLatch = new CountDownLatch(1); + CountDownLatch changedLatch = new CountDownLatch(1); + CountDownLatch removedLatch = new CountDownLatch(1); - addManagerCallback(new MediaRouter2Manager.Callback() { - @Override - public void onRoutesRemoved(List<MediaRoute2Info> routes) { - assertTrue(routes.size() > 0); - for (MediaRoute2Info route : routes) { - if (route.getOriginalId().equals(ROUTE_ID2) - && route.getName().equals(ROUTE_NAME2)) { - removedLatch.countDown(); + addManagerCallback( + new MediaRouter2Manager.Callback() { + @Override + public void onRoutesUpdated() { + if (addedLatch.getCount() == 1 + && checkRoutesMatch(mManager.getAllRoutes(), routes)) { + addedLatch.countDown(); + } else if (changedLatch.getCount() == 1 + && checkRoutesMatch( + mManager.getAllRoutes(), routes.subList(1, 2))) { + changedLatch.countDown(); + } else if (removedLatch.getCount() == 1 + && checkRoutesRemoved(mManager.getAllRoutes(), routes)) { + removedLatch.countDown(); + } } - } - } - @Override - public void onRoutesAdded(List<MediaRoute2Info> routes) { - assertTrue(routes.size() > 0); - if (removedLatch.getCount() > 0) { - return; - } - for (MediaRoute2Info route : routes) { - if (route.getOriginalId().equals(ROUTE_ID2) - && route.getName().equals(ROUTE_NAME2)) { - addedLatch.countDown(); - } - } - } - }); + }); - MediaRoute2Info routeToRemove = routes.get(ROUTE_ID2); - assertNotNull(routeToRemove); + mService.addRoutes(routes); + assertTrue( + "Added routes not found or onRoutesUpdated() never called.", + addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - mService.removeRoute(ROUTE_ID2); - assertTrue(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + MediaRoute2Info newRoute2 = + new MediaRoute2Info.Builder(routes.get(1)) + .setConnectionState(newConnectionState) + .build(); + routes.set(1, newRoute2); + mService.addRoute(routes.get(1)); + assertTrue( + "Modified route not found or onRoutesUpdated() never called.", + changedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + List<String> routeIds = new ArrayList<>(); + routeIds.add(routeId0); + routeIds.add(routeId1); + + mService.removeRoutes(routeIds); + assertTrue( + "Removed routes not found or onRoutesUpdated() never called.", + removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } - mService.addRoute(routeToRemove); - assertTrue(addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + private static boolean checkRoutesMatch( + List<MediaRoute2Info> routesReceived, List<MediaRoute2Info> expectedRoutes) { + for (MediaRoute2Info expectedRoute : expectedRoutes) { + MediaRoute2Info matchingRoute = + routesReceived.stream() + .filter(r -> r.getOriginalId().equals(expectedRoute.getOriginalId())) + .findFirst() + .orElse(null); + + if (matchingRoute == null) { + return false; + } + assertTrue(TextUtils.equals(expectedRoute.getName(), matchingRoute.getName())); + assertEquals(expectedRoute.getFeatures(), matchingRoute.getFeatures()); + assertEquals(expectedRoute.getConnectionState(), matchingRoute.getConnectionState()); + } + + return true; + } + + private static boolean checkRoutesRemoved( + List<MediaRoute2Info> routesReceived, List<MediaRoute2Info> routesRemoved) { + for (MediaRoute2Info removedRoute : routesRemoved) { + if (routesReceived.stream() + .anyMatch(r -> r.getOriginalId().equals(removedRoute.getOriginalId()))) { + return false; + } + } + return true; } @Test @@ -874,28 +918,31 @@ public class MediaRouter2ManagerTest { // A dummy callback is required to send route feature info. RouteCallback routeCallback = new RouteCallback() {}; - MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() { - @Override - public void onRoutesAdded(List<MediaRoute2Info> routes) { - for (MediaRoute2Info route : routes) { - if (!route.isSystemRoute() - && hasMatchingFeature(route.getFeatures(), preference - .getPreferredFeatures())) { - addedLatch.countDown(); - break; + MediaRouter2Manager.Callback managerCallback = + new MediaRouter2Manager.Callback() { + @Override + public void onRoutesUpdated() { + List<MediaRoute2Info> routes = mManager.getAllRoutes(); + for (MediaRoute2Info route : routes) { + if (!route.isSystemRoute() + && hasMatchingFeature( + route.getFeatures(), + preference.getPreferredFeatures())) { + addedLatch.countDown(); + break; + } + } } - } - } - @Override - public void onDiscoveryPreferenceChanged(String packageName, - RouteDiscoveryPreference discoveryPreference) { - if (TextUtils.equals(mPackageName, packageName) - && Objects.equals(preference, discoveryPreference)) { - preferenceLatch.countDown(); - } - } - }; + @Override + public void onDiscoveryPreferenceChanged( + String packageName, RouteDiscoveryPreference discoveryPreference) { + if (TextUtils.equals(mPackageName, packageName) + && Objects.equals(preference, discoveryPreference)) { + preferenceLatch.countDown(); + } + } + }; mManager.registerCallback(mExecutor, managerCallback); mRouter2.registerRouteCallback(mExecutor, routeCallback, preference); @@ -923,15 +970,17 @@ public class MediaRouter2ManagerTest { void awaitOnRouteChangedManager(Runnable task, String routeId, Predicate<MediaRoute2Info> predicate) throws Exception { CountDownLatch latch = new CountDownLatch(1); - MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() { - @Override - public void onRoutesChanged(List<MediaRoute2Info> changed) { - MediaRoute2Info route = createRouteMap(changed).get(routeId); - if (route != null && predicate.test(route)) { - latch.countDown(); - } - } - }; + MediaRouter2Manager.Callback callback = + new MediaRouter2Manager.Callback() { + @Override + public void onRoutesUpdated() { + MediaRoute2Info route = + createRouteMap(mManager.getAllRoutes()).get(routeId); + if (route != null && predicate.test(route)) { + latch.countDown(); + } + } + }; mManager.registerCallback(mExecutor, callback); try { task.run(); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java index a51e3714b6f7..a7ae5f45b795 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java @@ -30,7 +30,9 @@ import android.os.Bundle; import android.os.IBinder; import android.text.TextUtils; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -146,19 +148,44 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService { * they have the same route id. */ public void addRoute(@NonNull MediaRoute2Info route) { - Objects.requireNonNull(route, "route must not be null"); - mRoutes.put(route.getOriginalId(), route); - publishRoutes(); + addRoutes(Collections.singletonList(route)); } /** - * Removes a route and publishes it. + * Adds a list of routes and publishes it. It will replace existing routes with matching ids. + * + * @param routes list of routes to be added. */ + public void addRoutes(@NonNull List<MediaRoute2Info> routes) { + Objects.requireNonNull(routes, "Routes must not be null."); + for (MediaRoute2Info route : routes) { + Objects.requireNonNull(route, "Route must not be null"); + mRoutes.put(route.getOriginalId(), route); + } + publishRoutes(); + } + + /** Removes a route and publishes it. */ public void removeRoute(@NonNull String routeId) { - Objects.requireNonNull(routeId, "routeId must not be null"); - MediaRoute2Info route = mRoutes.get(routeId); - if (route != null) { - mRoutes.remove(routeId); + removeRoutes(Collections.singletonList(routeId)); + } + + /** + * Removes a list of routes and publishes the changes. + * + * @param routes list of route ids to be removed. + */ + public void removeRoutes(@NonNull List<String> routes) { + Objects.requireNonNull(routes, "Routes must not be null"); + boolean hasRemovedRoutes = false; + for (String routeId : routes) { + MediaRoute2Info route = mRoutes.get(routeId); + if (route != null) { + mRoutes.remove(routeId); + hasRemovedRoutes = true; + } + } + if (hasRemovedRoutes) { publishRoutes(); } } diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS index b352b045c352..288787241caa 100644 --- a/packages/SettingsLib/Spa/OWNERS +++ b/packages/SettingsLib/Spa/OWNERS @@ -1,6 +1,6 @@ +set noparent + chaohuiw@google.com hanxu@google.com kellyz@google.com pierreqian@google.com - -per-file *.xml = set noparent diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle index 8c97eca548b5..d38013679ad4 100644 --- a/packages/SettingsLib/Spa/build.gradle +++ b/packages/SettingsLib/Spa/build.gradle @@ -16,9 +16,9 @@ buildscript { ext { - minSdk_version = 31 - compose_version = '1.2.0-alpha04' - compose_material3_version = '1.0.0-alpha06' + spa_min_sdk = 31 + jetpack_compose_version = '1.2.0-alpha04' + jetpack_compose_material3_version = '1.0.0-alpha06' } } plugins { diff --git a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml index 9a89e5efdddb..36b93134bdcb 100644 --- a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml +++ b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml @@ -13,14 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - --> +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib.spa.codelab"> <application android:label="@string/app_name" android:supportsRtl="true" - android:theme="@style/Theme.SettingsLib.Compose.DayNight"> + android:theme="@style/Theme.SpaLib.DayNight"> <activity android:name="com.android.settingslib.spa.codelab.MainActivity" android:exported="true"> diff --git a/packages/SettingsLib/Spa/codelab/build.gradle b/packages/SettingsLib/Spa/codelab/build.gradle index 5251ddd8c01d..169ecf08aeac 100644 --- a/packages/SettingsLib/Spa/codelab/build.gradle +++ b/packages/SettingsLib/Spa/codelab/build.gradle @@ -25,7 +25,7 @@ android { defaultConfig { applicationId "com.android.settingslib.spa.codelab" - minSdk minSdk_version + minSdk spa_min_sdk targetSdk 33 versionCode 1 versionName "1.0" @@ -52,7 +52,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion compose_version + kotlinCompilerExtensionVersion jetpack_compose_version } packagingOptions { resources { diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt index 484b6045f443..5c6b609b25a4 100644 --- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt @@ -22,10 +22,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavType import androidx.navigation.navArgument -import com.android.settingslib.spa.api.SettingsPageProvider -import com.android.settingslib.spa.framework.navigator -import com.android.settingslib.spa.framework.toState -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt new file mode 100644 index 000000000000..9fcbff843203 --- /dev/null +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt @@ -0,0 +1,71 @@ +/* + * 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.settingslib.spa.codelab.page + +import android.os.Bundle +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.stateOf +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.ui.Footer + +object FooterPageProvider : SettingsPageProvider { + override val name = "Footer" + + @Composable + override fun Page(arguments: Bundle?) { + FooterPage() + } + + @Composable + fun EntryItem() { + Preference(object : PreferenceModel { + override val title = "Sample Footer" + override val onClick = navigator(name) + }) + } +} + +@Composable +private fun FooterPage() { + Column(Modifier.verticalScroll(rememberScrollState())) { + Preference(remember { + object : PreferenceModel { + override val title = "Some Preference" + override val summary = stateOf("Some summary") + } + }) + Footer(footerText = "Footer text always at the end of page.") + } +} + +@Preview(showBackground = true) +@Composable +private fun FooterPagePreview() { + SettingsTheme { + FooterPage() + } +} diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt index 27c70e450555..57a69c4be22f 100644 --- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt @@ -25,10 +25,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.android.settingslib.spa.api.SettingsPageProvider import com.android.settingslib.spa.codelab.R -import com.android.settingslib.spa.theme.SettingsDimension -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsTheme object HomePageProvider : SettingsPageProvider { override val name = Destinations.Home @@ -50,8 +50,11 @@ private fun HomePage() { ) PreferencePageProvider.EntryItem() - + SwitchPreferencePageProvider.EntryItem() ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0) + + SliderPageProvider.EntryItem() + FooterPageProvider.EntryItem() } } diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt index 862fc1ee3151..54c588ab18f0 100644 --- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt @@ -16,15 +16,24 @@ package com.android.settingslib.spa.codelab.page -import com.android.settingslib.spa.api.SettingsPageRepository +import com.android.settingslib.spa.framework.api.SettingsPageRepository object Destinations { const val Home = "Home" const val Preference = "Preference" + const val SwitchPreference = "SwitchPreference" const val Argument = "Argument" + const val Slider = "Slider" } val codelabPageRepository = SettingsPageRepository( - allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider), + allPages = listOf( + HomePageProvider, + PreferencePageProvider, + SwitchPreferencePageProvider, + ArgumentPageProvider, + SliderPageProvider, + FooterPageProvider, + ), startDestination = Destinations.Home, ) diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt index 81627f60168e..d53562d31566 100644 --- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt @@ -30,10 +30,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.android.settingslib.spa.api.SettingsPageProvider -import com.android.settingslib.spa.framework.navigator -import com.android.settingslib.spa.framework.toState -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel import kotlinx.coroutines.delay diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt new file mode 100644 index 000000000000..6e965813afc4 --- /dev/null +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt @@ -0,0 +1,107 @@ +/* + * 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.settingslib.spa.codelab.page + +import android.os.Bundle +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AccessAlarm +import androidx.compose.material.icons.outlined.MusicNote +import androidx.compose.material.icons.outlined.MusicOff +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.ui.SettingsSlider +import com.android.settingslib.spa.widget.ui.SettingsSliderModel + +object SliderPageProvider : SettingsPageProvider { + override val name = Destinations.Slider + + @Composable + override fun Page(arguments: Bundle?) { + SliderPage() + } + + @Composable + fun EntryItem() { + Preference(object : PreferenceModel { + override val title = "Sample Slider" + override val onClick = navigator(Destinations.Slider) + }) + } +} + +@Composable +private fun SliderPage() { + Column(Modifier.verticalScroll(rememberScrollState())) { + SettingsSlider(object : SettingsSliderModel { + override val title = "Slider" + override val initValue = 40 + }) + + SettingsSlider(object : SettingsSliderModel { + override val title = "Slider with icon" + override val initValue = 30 + override val onValueChangeFinished = { + println("onValueChangeFinished") + } + override val icon = Icons.Outlined.AccessAlarm + }) + + val initValue = 0 + var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) } + var sliderPosition by remember { mutableStateOf(initValue) } + SettingsSlider(object : SettingsSliderModel { + override val title = "Slider with changeable icon" + override val initValue = initValue + override val onValueChange = { it: Int -> + sliderPosition = it + icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff + } + override val onValueChangeFinished = { + println("onValueChangeFinished: the value is $sliderPosition") + } + override val icon = icon + }) + + SettingsSlider(object : SettingsSliderModel { + override val title = "Slider with steps" + override val initValue = 2 + override val valueRange = 1..5 + override val showSteps = true + }) + } +} + +@Preview(showBackground = true) +@Composable +private fun SliderPagePreview() { + SettingsTheme { + SliderPage() + } +} diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt new file mode 100644 index 000000000000..b566afa84d42 --- /dev/null +++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt @@ -0,0 +1,129 @@ +/* + * 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.settingslib.spa.codelab.page + +import android.os.Bundle +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.stateOf +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spa.widget.preference.SwitchPreference +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import kotlinx.coroutines.delay + +object SwitchPreferencePageProvider : SettingsPageProvider { + override val name = Destinations.SwitchPreference + + @Composable + override fun Page(arguments: Bundle?) { + SwitchPreferencePage() + } + + @Composable + fun EntryItem() { + Preference(object : PreferenceModel { + override val title = "Sample SwitchPreference" + override val onClick = navigator(Destinations.SwitchPreference) + }) + } +} + +@Composable +private fun SwitchPreferencePage() { + Column(Modifier.verticalScroll(rememberScrollState())) { + SampleSwitchPreference() + SampleSwitchPreferenceWithSummary() + SampleSwitchPreferenceWithAsyncSummary() + SampleNotChangeableSwitchPreference() + } +} + +@Composable +private fun SampleSwitchPreference() { + val checked = rememberSaveable { mutableStateOf(false) } + SwitchPreference(remember { + object : SwitchPreferenceModel { + override val title = "SwitchPreference" + override val checked = checked + override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked } + } + }) +} + +@Composable +private fun SampleSwitchPreferenceWithSummary() { + val checked = rememberSaveable { mutableStateOf(true) } + SwitchPreference(remember { + object : SwitchPreferenceModel { + override val title = "SwitchPreference" + override val summary = stateOf("With summary") + override val checked = checked + override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked } + } + }) +} + +@Composable +private fun SampleSwitchPreferenceWithAsyncSummary() { + val checked = rememberSaveable { mutableStateOf(true) } + val summary = produceState(initialValue = " ") { + delay(1000L) + value = "Async summary" + } + SwitchPreference(remember { + object : SwitchPreferenceModel { + override val title = "SwitchPreference" + override val summary = summary + override val checked = checked + override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked } + } + }) +} + +@Composable +private fun SampleNotChangeableSwitchPreference() { + val checked = rememberSaveable { mutableStateOf(true) } + SwitchPreference(remember { + object : SwitchPreferenceModel { + override val title = "SwitchPreference" + override val summary = stateOf("Not changeable") + override val changeable = stateOf(false) + override val checked = checked + override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked } + } + }) +} + +@Preview(showBackground = true) +@Composable +private fun SwitchPreferencePagePreview() { + SettingsTheme { + SwitchPreferencePage() + } +} diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp index 463c0765943f..3c8d91edc05b 100644 --- a/packages/SettingsLib/Spa/spa/Android.bp +++ b/packages/SettingsLib/Spa/spa/Android.bp @@ -31,6 +31,9 @@ android_library { "androidx.navigation_navigation-compose", "com.google.android.material_material", ], - kotlincflags: ["-Xjvm-default=all"], + kotlincflags: [ + "-Xjvm-default=all", + "-Xopt-in=kotlin.RequiresOptIn", + ], min_sdk_version: "31", } diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle index 60794c88d00d..ad69da314735 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle +++ b/packages/SettingsLib/Spa/spa/build.gradle @@ -24,7 +24,7 @@ android { compileSdk 33 defaultConfig { - minSdk minSdk_version + minSdk spa_min_sdk targetSdk 33 } @@ -43,13 +43,13 @@ android { } kotlinOptions { jvmTarget = '1.8' - freeCompilerArgs = ["-Xjvm-default=all"] + freeCompilerArgs = ["-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn"] } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion compose_version + kotlinCompilerExtensionVersion jetpack_compose_version } packagingOptions { resources { @@ -59,11 +59,11 @@ android { } dependencies { - api "androidx.compose.material3:material3:$compose_material3_version" - api "androidx.compose.material:material-icons-extended:$compose_version" - api "androidx.compose.runtime:runtime-livedata:$compose_version" - api "androidx.compose.ui:ui-tooling-preview:$compose_version" + api "androidx.compose.material3:material3:$jetpack_compose_material3_version" + api "androidx.compose.material:material-icons-extended:$jetpack_compose_version" + api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version" + api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version" api 'androidx.navigation:navigation-compose:2.5.0' api 'com.google.android.material:material:1.6.1' - debugApi "androidx.compose.ui:ui-tooling:$compose_version" + debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version" } diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml index 8b52b507bdd9..67dd2b0cc5e0 100644 --- a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml +++ b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml @@ -13,8 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - --> +--> <resources> - <style name="Theme.SettingsLib.Compose.DayNight" /> + <style name="Theme.SpaLib.DayNight" /> </resources> diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml index 01f9ea592f6d..e0e5fc211ec6 100644 --- a/packages/SettingsLib/Spa/spa/res/values/themes.xml +++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml @@ -13,15 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - --> +--> <resources> - <style name="Theme.SettingsLib.Compose" parent="Theme.Material3.DayNight.NoActionBar"> + <style name="Theme.SpaLib" parent="Theme.Material3.DayNight.NoActionBar"> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:navigationBarColor">@android:color/transparent</item> </style> - <style name="Theme.SettingsLib.Compose.DayNight"> + <style name="Theme.SpaLib.DayNight"> <item name="android:windowLightStatusBar">true</item> </style> </resources> diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt index 28a5899e0141..5b39b6e08e6a 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt @@ -24,9 +24,10 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.android.settingslib.spa.api.SettingsPageProvider -import com.android.settingslib.spa.api.SettingsPageRepository -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.api.SettingsPageProvider +import com.android.settingslib.spa.framework.api.SettingsPageRepository +import com.android.settingslib.spa.framework.compose.localNavController +import com.android.settingslib.spa.framework.theme.SettingsTheme open class SpaActivity( private val settingsPageRepository: SettingsPageRepository, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageProvider.kt index 0ad0003cca64..84daf224fa10 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.spa.api +package com.android.settingslib.spa.framework.api import android.os.Bundle import androidx.compose.runtime.Composable diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageRepository.kt index ce39f4fbfa2e..4a270b128eb7 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.spa.api +package com.android.settingslib.spa.framework.api data class SettingsPageRepository( val allPages: List<SettingsPageProvider>, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt new file mode 100644 index 000000000000..ae325f8862eb --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt @@ -0,0 +1,180 @@ +/* + * 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.settingslib.spa.framework.compose + +import android.graphics.drawable.Animatable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.compose.runtime.Composable +import androidx.compose.runtime.RememberObserver +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.asAndroidColorFilter +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.ColorPainter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.withSave +import androidx.compose.ui.unit.LayoutDirection +import kotlin.math.roundToInt + +/** + * ************************************************************************************************* + * This file was forked from + * https://github.com/google/accompanist/blob/main/drawablepainter/src/main/java/com/google/accompanist/drawablepainter/DrawablePainter.kt + * and will be removed once it lands in AndroidX. + */ + +private val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) { + Handler(Looper.getMainLooper()) +} + +/** + * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances + * should be remembered to be able to start and stop [Animatable] animations. + * + * Instances are usually retrieved from [rememberDrawablePainter]. + */ +class DrawablePainter( + val drawable: Drawable +) : Painter(), RememberObserver { + private var drawInvalidateTick by mutableStateOf(0) + private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize) + + private val callback: Drawable.Callback by lazy { + object : Drawable.Callback { + override fun invalidateDrawable(d: Drawable) { + // Update the tick so that we get re-drawn + drawInvalidateTick++ + // Update our intrinsic size too + drawableIntrinsicSize = drawable.intrinsicSize + } + + override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) { + MAIN_HANDLER.postAtTime(what, time) + } + + override fun unscheduleDrawable(d: Drawable, what: Runnable) { + MAIN_HANDLER.removeCallbacks(what) + } + } + } + + init { + if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) { + // Update the drawable's bounds to match the intrinsic size + drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) + } + } + + override fun onRemembered() { + drawable.callback = callback + drawable.setVisible(true, true) + if (drawable is Animatable) drawable.start() + } + + override fun onAbandoned() = onForgotten() + + override fun onForgotten() { + if (drawable is Animatable) drawable.stop() + drawable.setVisible(false, false) + drawable.callback = null + } + + override fun applyAlpha(alpha: Float): Boolean { + drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255) + return true + } + + override fun applyColorFilter(colorFilter: ColorFilter?): Boolean { + drawable.colorFilter = colorFilter?.asAndroidColorFilter() + return true + } + + override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean = + drawable.setLayoutDirection( + when (layoutDirection) { + LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR + LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL + } + ) + + override val intrinsicSize: Size get() = drawableIntrinsicSize + + override fun DrawScope.onDraw() { + drawIntoCanvas { canvas -> + // Reading this ensures that we invalidate when invalidateDrawable() is called + drawInvalidateTick + + // Update the Drawable's bounds + drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt()) + + canvas.withSave { + drawable.draw(canvas.nativeCanvas) + } + } + } +} + +/** + * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the + * drawable contents and use Compose primitives where possible. + * + * If the provided [drawable] is `null`, an empty no-op painter is returned. + * + * This function tries to dispatch lifecycle events to [drawable] as much as possible from + * within Compose. + * + * @sample com.google.accompanist.sample.drawablepainter.BasicSample + */ +@Composable +fun rememberDrawablePainter(drawable: Drawable?): Painter = remember(drawable) { + when (drawable) { + null -> EmptyPainter + is BitmapDrawable -> BitmapPainter(drawable.bitmap.asImageBitmap()) + is ColorDrawable -> ColorPainter(Color(drawable.color)) + // Since the DrawablePainter will be remembered and it implements RememberObserver, it + // will receive the necessary events + else -> DrawablePainter(drawable.mutate()) + } +} + +private val Drawable.intrinsicSize: Size + get() = when { + // Only return a finite size if the drawable has an intrinsic size + intrinsicWidth >= 0 && intrinsicHeight >= 0 -> { + Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat()) + } + else -> Size.Unspecified + } + +internal object EmptyPainter : Painter() { + override val intrinsicSize: Size get() = Size.Unspecified + override fun DrawScope.onDraw() {} +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt index 35112adf322c..c68d5de0d7c7 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.spa.framework +package com.android.settingslib.spa.framework.compose import androidx.compose.runtime.Composable import androidx.compose.runtime.compositionLocalOf diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt index 22d85e0cff88..ba8854653b0b 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt @@ -14,11 +14,19 @@ * limitations under the License. */ -package com.android.settingslib.spa.framework +package com.android.settingslib.spa.framework.compose +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext + +@Composable +fun <T> rememberContext(constructor: (Context) -> T): T { + val context = LocalContext.current + return remember(context) { constructor(context) } +} /** * Remember the [State] initialized with the [this]. diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt index 8cd9184317ca..4626f0bbed49 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.spa.theme +package com.android.settingslib.spa.framework.theme import androidx.compose.material3.ColorScheme import androidx.compose.material3.dynamicDarkColorScheme diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt new file mode 100644 index 000000000000..27fdc916a434 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt @@ -0,0 +1,91 @@ +/* + * 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.settingslib.spa.framework.theme + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext + +data class SettingsColorScheme( + val background: Color = Color.Unspecified, + val categoryTitle: Color = Color.Unspecified, + val surface: Color = Color.Unspecified, + val surfaceHeader: Color = Color.Unspecified, + val secondaryText: Color = Color.Unspecified, + val primaryContainer: Color = Color.Unspecified, + val onPrimaryContainer: Color = Color.Unspecified, +) + +internal val LocalColorScheme = staticCompositionLocalOf { SettingsColorScheme() } + +@Composable +internal fun settingsColorScheme(isDarkTheme: Boolean): SettingsColorScheme { + val context = LocalContext.current + return remember(isDarkTheme) { + when { + isDarkTheme -> dynamicDarkColorScheme(context) + else -> dynamicLightColorScheme(context) + } + } +} + +/** + * Creates a light dynamic color scheme. + * + * Use this function to create a color scheme based off the system wallpaper. If the developer + * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a + * light theme variant. + * + * @param context The context required to get system resource data. + */ +private fun dynamicLightColorScheme(context: Context): SettingsColorScheme { + val tonalPalette = dynamicTonalPalette(context) + return SettingsColorScheme( + background = tonalPalette.neutral95, + categoryTitle = tonalPalette.primary40, + surface = tonalPalette.neutral99, + surfaceHeader = tonalPalette.neutral90, + secondaryText = tonalPalette.neutralVariant30, + primaryContainer = tonalPalette.primary90, + onPrimaryContainer = tonalPalette.neutral10, + ) +} + +/** + * Creates a dark dynamic color scheme. + * + * Use this function to create a color scheme based off the system wallpaper. If the developer + * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a dark + * theme variant. + * + * @param context The context required to get system resource data. + */ +private fun dynamicDarkColorScheme(context: Context): SettingsColorScheme { + val tonalPalette = dynamicTonalPalette(context) + return SettingsColorScheme( + background = tonalPalette.neutral10, + categoryTitle = tonalPalette.primary90, + surface = tonalPalette.neutral20, + surfaceHeader = tonalPalette.neutral30, + secondaryText = tonalPalette.neutralVariant80, + primaryContainer = tonalPalette.secondary90, + onPrimaryContainer = tonalPalette.neutral10, + ) +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index 51e75c53e046..e1ca69bd7940 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.settingslib.spa.theme +package com.android.settingslib.spa.framework.theme import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.unit.dp object SettingsDimension { + val itemIconSize = 24.dp val itemIconContainerSize = 72.dp val itemPaddingStart = 24.dp val itemPaddingEnd = 16.dp diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt index 5f4da8af5cb4..04ee3c3be402 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settingslib.spa.theme +package com.android.settingslib.spa.framework.theme object SettingsOpacity { const val Full = 1f diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt new file mode 100644 index 000000000000..e6fa74e34cc8 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt @@ -0,0 +1,48 @@ +/* + * 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.settingslib.spa.framework.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable + +/** + * The Material 3 Theme for Settings. + */ +@Composable +fun SettingsTheme(content: @Composable () -> Unit) { + val isDarkTheme = isSystemInDarkTheme() + val settingsColorScheme = settingsColorScheme(isDarkTheme) + val colorScheme = materialColorScheme(isDarkTheme).copy( + background = settingsColorScheme.background, + ) + + CompositionLocalProvider(LocalColorScheme provides settingsColorScheme(isDarkTheme)) { + MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) { + content() + } + } +} + +object SettingsTheme { + val colorScheme: SettingsColorScheme + @Composable + @ReadOnlyComposable + get() = LocalColorScheme.current +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt new file mode 100644 index 000000000000..f81f5e734fb4 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt @@ -0,0 +1,205 @@ +/* + * 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.settingslib.spa.framework.theme + +import android.R +import android.content.Context +import androidx.annotation.ColorRes +import androidx.annotation.DoNotInline +import androidx.compose.ui.graphics.Color + +/** + * Tonal Palette structure in Material. + * + * A tonal palette is comprised of 5 tonal ranges. Each tonal range includes the 13 stops, or + * tonal swatches. + * + * Tonal range names are: + * - Neutral (N) + * - Neutral variant (NV) + * - Primary (P) + * - Secondary (S) + * - Tertiary (T) + */ +internal class SettingsTonalPalette( + // The neutral tonal range from the generated dynamic color palette. + // Ordered from the lightest shade [neutral100] to the darkest shade [neutral0]. + val neutral100: Color, + val neutral99: Color, + val neutral95: Color, + val neutral90: Color, + val neutral80: Color, + val neutral70: Color, + val neutral60: Color, + val neutral50: Color, + val neutral40: Color, + val neutral30: Color, + val neutral20: Color, + val neutral10: Color, + val neutral0: Color, + + // The neutral variant tonal range, sometimes called "neutral 2", from the + // generated dynamic color palette. + // Ordered from the lightest shade [neutralVariant100] to the darkest shade [neutralVariant0]. + val neutralVariant100: Color, + val neutralVariant99: Color, + val neutralVariant95: Color, + val neutralVariant90: Color, + val neutralVariant80: Color, + val neutralVariant70: Color, + val neutralVariant60: Color, + val neutralVariant50: Color, + val neutralVariant40: Color, + val neutralVariant30: Color, + val neutralVariant20: Color, + val neutralVariant10: Color, + val neutralVariant0: Color, + + // The primary tonal range from the generated dynamic color palette. + // Ordered from the lightest shade [primary100] to the darkest shade [primary0]. + val primary100: Color, + val primary99: Color, + val primary95: Color, + val primary90: Color, + val primary80: Color, + val primary70: Color, + val primary60: Color, + val primary50: Color, + val primary40: Color, + val primary30: Color, + val primary20: Color, + val primary10: Color, + val primary0: Color, + + // The secondary tonal range from the generated dynamic color palette. + // Ordered from the lightest shade [secondary100] to the darkest shade [secondary0]. + val secondary100: Color, + val secondary99: Color, + val secondary95: Color, + val secondary90: Color, + val secondary80: Color, + val secondary70: Color, + val secondary60: Color, + val secondary50: Color, + val secondary40: Color, + val secondary30: Color, + val secondary20: Color, + val secondary10: Color, + val secondary0: Color, + + // The tertiary tonal range from the generated dynamic color palette. + // Ordered from the lightest shade [tertiary100] to the darkest shade [tertiary0]. + val tertiary100: Color, + val tertiary99: Color, + val tertiary95: Color, + val tertiary90: Color, + val tertiary80: Color, + val tertiary70: Color, + val tertiary60: Color, + val tertiary50: Color, + val tertiary40: Color, + val tertiary30: Color, + val tertiary20: Color, + val tertiary10: Color, + val tertiary0: Color, +) + +/** Dynamic colors in Material. */ +internal fun dynamicTonalPalette(context: Context) = SettingsTonalPalette( + // The neutral tonal range from the generated dynamic color palette. + neutral100 = ColorResourceHelper.getColor(context, R.color.system_neutral1_0), + neutral99 = ColorResourceHelper.getColor(context, R.color.system_neutral1_10), + neutral95 = ColorResourceHelper.getColor(context, R.color.system_neutral1_50), + neutral90 = ColorResourceHelper.getColor(context, R.color.system_neutral1_100), + neutral80 = ColorResourceHelper.getColor(context, R.color.system_neutral1_200), + neutral70 = ColorResourceHelper.getColor(context, R.color.system_neutral1_300), + neutral60 = ColorResourceHelper.getColor(context, R.color.system_neutral1_400), + neutral50 = ColorResourceHelper.getColor(context, R.color.system_neutral1_500), + neutral40 = ColorResourceHelper.getColor(context, R.color.system_neutral1_600), + neutral30 = ColorResourceHelper.getColor(context, R.color.system_neutral1_700), + neutral20 = ColorResourceHelper.getColor(context, R.color.system_neutral1_800), + neutral10 = ColorResourceHelper.getColor(context, R.color.system_neutral1_900), + neutral0 = ColorResourceHelper.getColor(context, R.color.system_neutral1_1000), + + // The neutral variant tonal range, sometimes called "neutral 2", from the + // generated dynamic color palette. + neutralVariant100 = ColorResourceHelper.getColor(context, R.color.system_neutral2_0), + neutralVariant99 = ColorResourceHelper.getColor(context, R.color.system_neutral2_10), + neutralVariant95 = ColorResourceHelper.getColor(context, R.color.system_neutral2_50), + neutralVariant90 = ColorResourceHelper.getColor(context, R.color.system_neutral2_100), + neutralVariant80 = ColorResourceHelper.getColor(context, R.color.system_neutral2_200), + neutralVariant70 = ColorResourceHelper.getColor(context, R.color.system_neutral2_300), + neutralVariant60 = ColorResourceHelper.getColor(context, R.color.system_neutral2_400), + neutralVariant50 = ColorResourceHelper.getColor(context, R.color.system_neutral2_500), + neutralVariant40 = ColorResourceHelper.getColor(context, R.color.system_neutral2_600), + neutralVariant30 = ColorResourceHelper.getColor(context, R.color.system_neutral2_700), + neutralVariant20 = ColorResourceHelper.getColor(context, R.color.system_neutral2_800), + neutralVariant10 = ColorResourceHelper.getColor(context, R.color.system_neutral2_900), + neutralVariant0 = ColorResourceHelper.getColor(context, R.color.system_neutral2_1000), + + // The primary tonal range from the generated dynamic color palette. + primary100 = ColorResourceHelper.getColor(context, R.color.system_accent1_0), + primary99 = ColorResourceHelper.getColor(context, R.color.system_accent1_10), + primary95 = ColorResourceHelper.getColor(context, R.color.system_accent1_50), + primary90 = ColorResourceHelper.getColor(context, R.color.system_accent1_100), + primary80 = ColorResourceHelper.getColor(context, R.color.system_accent1_200), + primary70 = ColorResourceHelper.getColor(context, R.color.system_accent1_300), + primary60 = ColorResourceHelper.getColor(context, R.color.system_accent1_400), + primary50 = ColorResourceHelper.getColor(context, R.color.system_accent1_500), + primary40 = ColorResourceHelper.getColor(context, R.color.system_accent1_600), + primary30 = ColorResourceHelper.getColor(context, R.color.system_accent1_700), + primary20 = ColorResourceHelper.getColor(context, R.color.system_accent1_800), + primary10 = ColorResourceHelper.getColor(context, R.color.system_accent1_900), + primary0 = ColorResourceHelper.getColor(context, R.color.system_accent1_1000), + + // The secondary tonal range from the generated dynamic color palette. + secondary100 = ColorResourceHelper.getColor(context, R.color.system_accent2_0), + secondary99 = ColorResourceHelper.getColor(context, R.color.system_accent2_10), + secondary95 = ColorResourceHelper.getColor(context, R.color.system_accent2_50), + secondary90 = ColorResourceHelper.getColor(context, R.color.system_accent2_100), + secondary80 = ColorResourceHelper.getColor(context, R.color.system_accent2_200), + secondary70 = ColorResourceHelper.getColor(context, R.color.system_accent2_300), + secondary60 = ColorResourceHelper.getColor(context, R.color.system_accent2_400), + secondary50 = ColorResourceHelper.getColor(context, R.color.system_accent2_500), + secondary40 = ColorResourceHelper.getColor(context, R.color.system_accent2_600), + secondary30 = ColorResourceHelper.getColor(context, R.color.system_accent2_700), + secondary20 = ColorResourceHelper.getColor(context, R.color.system_accent2_800), + secondary10 = ColorResourceHelper.getColor(context, R.color.system_accent2_900), + secondary0 = ColorResourceHelper.getColor(context, R.color.system_accent2_1000), + + // The tertiary tonal range from the generated dynamic color palette. + tertiary100 = ColorResourceHelper.getColor(context, R.color.system_accent3_0), + tertiary99 = ColorResourceHelper.getColor(context, R.color.system_accent3_10), + tertiary95 = ColorResourceHelper.getColor(context, R.color.system_accent3_50), + tertiary90 = ColorResourceHelper.getColor(context, R.color.system_accent3_100), + tertiary80 = ColorResourceHelper.getColor(context, R.color.system_accent3_200), + tertiary70 = ColorResourceHelper.getColor(context, R.color.system_accent3_300), + tertiary60 = ColorResourceHelper.getColor(context, R.color.system_accent3_400), + tertiary50 = ColorResourceHelper.getColor(context, R.color.system_accent3_500), + tertiary40 = ColorResourceHelper.getColor(context, R.color.system_accent3_600), + tertiary30 = ColorResourceHelper.getColor(context, R.color.system_accent3_700), + tertiary20 = ColorResourceHelper.getColor(context, R.color.system_accent3_800), + tertiary10 = ColorResourceHelper.getColor(context, R.color.system_accent3_900), + tertiary0 = ColorResourceHelper.getColor(context, R.color.system_accent3_1000), +) + +private object ColorResourceHelper { + @DoNotInline + fun getColor(context: Context, @ColorRes id: Int): Color { + return Color(context.resources.getColor(id, context.theme)) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt new file mode 100644 index 000000000000..07f09ba95ca3 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt @@ -0,0 +1,144 @@ +/* + * 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.settingslib.spa.framework.theme + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp + +private class SettingsTypography { + private val brand = FontFamily.Default + private val plain = FontFamily.Default + + val typography = Typography( + displayLarge = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 57.sp, + lineHeight = 64.sp, + letterSpacing = (-0.2).sp + ), + displayMedium = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 45.sp, + lineHeight = 52.sp, + letterSpacing = 0.0.sp + ), + displaySmall = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 36.sp, + lineHeight = 44.sp, + letterSpacing = 0.0.sp + ), + headlineLarge = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 32.sp, + lineHeight = 40.sp, + letterSpacing = 0.0.sp + ), + headlineMedium = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 28.sp, + lineHeight = 36.sp, + letterSpacing = 0.0.sp + ), + headlineSmall = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 24.sp, + lineHeight = 32.sp, + letterSpacing = 0.0.sp + ), + titleLarge = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.02.em, + ), + titleMedium = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + lineHeight = 24.sp, + letterSpacing = 0.02.em, + ), + titleSmall = TextStyle( + fontFamily = brand, + fontWeight = FontWeight.Normal, + fontSize = 18.sp, + lineHeight = 20.sp, + letterSpacing = 0.02.em, + ), + bodyLarge = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.01.em, + ), + bodyMedium = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.01.em, + ), + bodySmall = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.01.em, + ), + labelLarge = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Medium, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.01.em, + ), + labelMedium = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.01.em, + ), + labelSmall = TextStyle( + fontFamily = plain, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + lineHeight = 16.sp, + letterSpacing = 0.01.em, + ), + ) +} + +@Composable +internal fun rememberSettingsTypography(): Typography { + return remember { SettingsTypography().typography } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt index d415e9b262e3..9a34dbf36735 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt @@ -25,8 +25,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.Divider -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.ui.Alignment @@ -35,10 +33,11 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.android.settingslib.spa.framework.toState -import com.android.settingslib.spa.theme.SettingsDimension -import com.android.settingslib.spa.theme.SettingsOpacity -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsOpacity +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.ui.SettingsTitle @Composable internal fun BaseLayout( @@ -94,11 +93,7 @@ private fun BaseIcon( @Composable private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) { Column(modifier) { - Text( - text = title, - color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.titleMedium, - ) + SettingsTitle(title) subTitle() } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt index 563a47a9a940..4b2c8e41a388 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt @@ -19,16 +19,15 @@ package com.android.settingslib.spa.widget.preference import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.BatteryChargingFull import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp -import com.android.settingslib.spa.framework.toState -import com.android.settingslib.spa.theme.SettingsDimension -import com.android.settingslib.spa.theme.SettingsTheme +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.ui.SettingsBody @Composable internal fun BasePreference( @@ -44,15 +43,7 @@ internal fun BasePreference( ) { BaseLayout( title = title, - subTitle = { - if (summary.value.isNotEmpty()) { - Text( - text = summary.value, - color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.bodyMedium, - ) - } - }, + subTitle = { SettingsBody(summary) }, modifier = modifier, icon = icon, enabled = enabled, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt index ee20280fb0cc..1a80ed20b031 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt @@ -20,7 +20,7 @@ import androidx.compose.foundation.clickable import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.ui.Modifier -import com.android.settingslib.spa.framework.stateOf +import com.android.settingslib.spa.framework.compose.stateOf /** * The widget model for [Preference] widget. diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt new file mode 100644 index 000000000000..0dab0dfe1d6d --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt @@ -0,0 +1,146 @@ +/* + * 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.settingslib.spa.widget.preference + +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.selection.toggleable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import com.android.settingslib.spa.framework.compose.stateOf +import com.android.settingslib.spa.framework.compose.toState +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.ui.SettingsSwitch + +/** + * The widget model for [SwitchPreference] widget. + */ +interface SwitchPreferenceModel { + /** + * The title of this [SwitchPreference]. + */ + val title: String + + /** + * The summary of this [SwitchPreference]. + */ + val summary: State<String> + get() = stateOf("") + + /** + * Indicates whether this [SwitchPreference] is checked. + * + * This can be `null` during the data loading before the data is available. + */ + val checked: State<Boolean?> + + /** + * Indicates whether this [SwitchPreference] is changeable. + * + * Not changeable [SwitchPreference] will be displayed in disabled style. + */ + val changeable: State<Boolean> + get() = stateOf(true) + + /** + * The switch change handler of this [SwitchPreference]. + * + * If `null`, this [SwitchPreference] is not [toggleable]. + */ + val onCheckedChange: ((newChecked: Boolean) -> Unit)? +} + +/** + * SwitchPreference widget. + * + * Data is provided through [SwitchPreferenceModel]. + */ +@Composable +fun SwitchPreference(model: SwitchPreferenceModel) { + InternalSwitchPreference( + title = model.title, + summary = model.summary, + checked = model.checked, + changeable = model.changeable, + onCheckedChange = model.onCheckedChange, + ) +} + +@Composable +internal fun InternalSwitchPreference( + title: String, + summary: State<String> = "".toState(), + checked: State<Boolean?>, + changeable: State<Boolean> = true.toState(), + paddingStart: Dp = SettingsDimension.itemPaddingStart, + paddingEnd: Dp = SettingsDimension.itemPaddingEnd, + paddingVertical: Dp = SettingsDimension.itemPaddingVertical, + onCheckedChange: ((newChecked: Boolean) -> Unit)?, +) { + val checkedValue = checked.value + val indication = LocalIndication.current + val modifier = remember(checkedValue) { + if (checkedValue != null && onCheckedChange != null) { + Modifier.toggleable( + value = checkedValue, + interactionSource = MutableInteractionSource(), + indication = indication, + enabled = changeable.value, + role = Role.Switch, + onValueChange = onCheckedChange, + ) + } else Modifier + } + BasePreference( + title = title, + summary = summary, + modifier = modifier, + enabled = changeable, + paddingStart = paddingStart, + paddingEnd = paddingEnd, + paddingVertical = paddingVertical, + ) { + SettingsSwitch(checked = checked, changeable = changeable) + } +} + +@Preview +@Composable +private fun SwitchPreferencePreview() { + SettingsTheme { + Column { + InternalSwitchPreference( + title = "Use Dark theme", + checked = true.toState(), + onCheckedChange = {}, + ) + InternalSwitchPreference( + title = "Use Dark theme", + summary = "Summary".toState(), + checked = false.toState(), + onCheckedChange = {}, + ) + } + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt new file mode 100644 index 000000000000..41fd03b08563 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt @@ -0,0 +1,47 @@ +/* + * 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.settingslib.spa.widget.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Info +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsTheme + +@Composable +fun Footer(footerText: String) { + if (footerText.isEmpty()) return + Column(Modifier.padding(SettingsDimension.itemPadding)) { + SettingsIcon(imageVector = Icons.Outlined.Info, contentDescription = null) + Spacer(modifier = Modifier.height(SettingsDimension.itemPaddingVertical)) + SettingsBody(footerText) + } +} + +@Preview +@Composable +private fun FooterPreview() { + SettingsTheme { + Footer("Footer text always at the end of page.") + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt new file mode 100644 index 000000000000..cb08cdb20885 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt @@ -0,0 +1,38 @@ +/* + * 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.settingslib.spa.widget.ui + +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +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 + +@Composable +fun SettingsIcon( + imageVector: ImageVector, + contentDescription: String?, +) { + Icon( + imageVector = imageVector, + contentDescription = contentDescription, + modifier = Modifier.size(SettingsDimension.itemIconSize), + tint = MaterialTheme.colorScheme.onSurface, + ) +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt new file mode 100644 index 000000000000..0454ac37cfb7 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt @@ -0,0 +1,185 @@ +/* + * 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.settingslib.spa.widget.ui + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AccessAlarm +import androidx.compose.material.icons.outlined.MusicNote +import androidx.compose.material.icons.outlined.MusicOff +import androidx.compose.material3.Icon +import androidx.compose.material3.Slider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.preference.BaseLayout +import kotlin.math.roundToInt + +/** + * The widget model for [SettingsSlider] widget. + */ +interface SettingsSliderModel { + /** + * The title of this [SettingsSlider]. + */ + val title: String + + /** + * The initial position of the [SettingsSlider]. + */ + val initValue: Int + + /** + * The value range for this [SettingsSlider]. + */ + val valueRange: IntRange + get() = 0..100 + + /** + * The lambda to be invoked during the value change by dragging or a click. This callback is + * used to get the real time value of the [SettingsSlider]. + */ + val onValueChange: ((value: Int) -> Unit)? + get() = null + + /** + * The lambda to be invoked when value change has ended. This callback is used to get when the + * user has completed selecting a new value by ending a drag or a click. + */ + val onValueChangeFinished: (() -> Unit)? + get() = null + + /** + * The icon image for [SettingsSlider]. If not specified, the slider hides the icon by default. + */ + val icon: ImageVector? + get() = null + + /** + * Indicates whether to show step marks. If show step marks, when user finish sliding, + * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides + * the step marks by default. + * + * The step is fixed to 1. + */ + val showSteps: Boolean + get() = false +} + +/** + * Settings slider widget. + * + * Data is provided through [SettingsSliderModel]. + */ +@Composable +fun SettingsSlider(model: SettingsSliderModel) { + SettingsSlider( + title = model.title, + initValue = model.initValue, + valueRange = model.valueRange, + onValueChange = model.onValueChange, + onValueChangeFinished = model.onValueChangeFinished, + icon = model.icon, + showSteps = model.showSteps, + ) +} + +@Composable +internal fun SettingsSlider( + title: String, + initValue: Int, + valueRange: IntRange = 0..100, + onValueChange: ((value: Int) -> Unit)? = null, + onValueChangeFinished: (() -> Unit)? = null, + icon: ImageVector? = null, + showSteps: Boolean = false, + modifier: Modifier = Modifier, +) { + var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) } + BaseLayout( + title = title, + subTitle = { + Slider( + value = sliderPosition, + onValueChange = { + sliderPosition = it + onValueChange?.invoke(sliderPosition.roundToInt()) + }, + modifier = modifier, + valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(), + steps = if (showSteps) (valueRange.count() - 2) else 0, + onValueChangeFinished = onValueChangeFinished, + ) + }, + icon = if (icon != null) ({ + Icon(imageVector = icon, contentDescription = null) + }) else null, + ) +} + +@Preview +@Composable +private fun SettingsSliderPreview() { + SettingsTheme { + val initValue = 30 + var sliderPosition by rememberSaveable { mutableStateOf(initValue) } + SettingsSlider( + title = "Alarm Volume", + initValue = 30, + onValueChange = { sliderPosition = it }, + onValueChangeFinished = { + println("onValueChangeFinished: the value is $sliderPosition") + }, + icon = Icons.Outlined.AccessAlarm, + ) + } +} + +@Preview +@Composable +private fun SettingsSliderIconChangePreview() { + SettingsTheme { + var icon by remember { mutableStateOf(Icons.Outlined.MusicNote) } + SettingsSlider( + title = "Media Volume", + initValue = 40, + onValueChange = { it: Int -> + icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff + }, + icon = icon, + ) + } +} + +@Preview +@Composable +private fun SettingsSliderStepsPreview() { + SettingsTheme { + SettingsSlider( + title = "Display Text", + initValue = 2, + valueRange = 1..5, + showSteps = true, + ) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt new file mode 100644 index 000000000000..45d5f6baa9cb --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt @@ -0,0 +1,47 @@ +/* + * 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.settingslib.spa.widget.ui + +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsSwitch( + checked: State<Boolean?>, + changeable: State<Boolean>, + onCheckedChange: ((newChecked: Boolean) -> Unit)? = null, +) { + // TODO: Replace Checkbox with Switch when the androidx.compose.material3_material3 library is + // updated to date. + val checkedValue = checked.value + if (checkedValue != null) { + Checkbox( + checked = checkedValue, + onCheckedChange = onCheckedChange, + enabled = changeable.value, + ) + } else { + Checkbox( + checked = false, + onCheckedChange = null, + enabled = false, + ) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt new file mode 100644 index 000000000000..a414c89dc24c --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt @@ -0,0 +1,52 @@ +/* + * 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.settingslib.spa.widget.ui + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State + +@Composable +fun SettingsTitle(title: State<String>) { + SettingsTitle(title.value) +} + +@Composable +fun SettingsTitle(title: String) { + Text( + text = title, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.titleMedium, + ) +} + +@Composable +fun SettingsBody(body: State<String>) { + SettingsBody(body.value) +} + +@Composable +fun SettingsBody(body: String) { + if (body.isNotEmpty()) { + Text( + text = body, + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium, + ) + } +} diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle index 707017e7e17f..be5a5ec40c4f 100644 --- a/packages/SettingsLib/Spa/tests/build.gradle +++ b/packages/SettingsLib/Spa/tests/build.gradle @@ -24,7 +24,7 @@ android { compileSdk 33 defaultConfig { - minSdk minSdk_version + minSdk spa_min_sdk targetSdk 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -50,7 +50,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion compose_version + kotlinCompilerExtensionVersion jetpack_compose_version } packagingOptions { resources { @@ -62,6 +62,6 @@ android { dependencies { androidTestImplementation(project(":spa")) androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3' - androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") - androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + androidTestImplementation("androidx.compose.ui:ui-test-junit4:$jetpack_compose_version") + androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$jetpack_compose_version" } diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt index 4097946abc79..a92f8713308f 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.framework.toState +import com.android.settingslib.spa.framework.compose.toState import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt new file mode 100644 index 000000000000..d6c8fbc9dc9e --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt @@ -0,0 +1,91 @@ +/* + * 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.settingslib.spa.widget.preference + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsOff +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.isToggleable +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.framework.compose.stateOf +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SwitchPreferenceTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + TestSwitchPreference(changeable = true) + } + + composeTestRule.onNodeWithText("SwitchPreference").assertIsDisplayed() + } + + @Test + fun toggleable_initialStateIsCorrect() { + composeTestRule.setContent { + TestSwitchPreference(changeable = true) + } + + composeTestRule.onNode(isToggleable()).assertIsOff() + } + + @Test + fun click_changeable_withEffect() { + composeTestRule.setContent { + TestSwitchPreference(changeable = true) + } + + composeTestRule.onNodeWithText("SwitchPreference").performClick() + composeTestRule.onNode(isToggleable()).assertIsOn() + } + + @Test + fun click_notChangeable_noEffect() { + composeTestRule.setContent { + TestSwitchPreference(changeable = false) + } + + composeTestRule.onNodeWithText("SwitchPreference").performClick() + composeTestRule.onNode(isToggleable()).assertIsOff() + } +} + +@Composable +private fun TestSwitchPreference(changeable: Boolean) { + val checked = rememberSaveable { mutableStateOf(false) } + SwitchPreference(remember { + object : SwitchPreferenceModel { + override val title = "SwitchPreference" + override val checked = checked + override val changeable = stateOf(changeable) + override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked } + } + }) +} diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp new file mode 100644 index 000000000000..48f7ff270ac7 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/Android.bp @@ -0,0 +1,33 @@ +// +// 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 { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "SpaPrivilegedLib", + + srcs: ["src/**/*.kt"], + + static_libs: [ + "SpaLib", + "SettingsLib", + "androidx.compose.runtime_runtime", + ], + kotlincflags: ["-Xjvm-default=all"], + min_sdk_version: "31", +} diff --git a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml new file mode 100644 index 000000000000..2efa10744bb3 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml @@ -0,0 +1,18 @@ +<?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. +--> + +<manifest package="com.android.settingslib.spaprivileged" /> diff --git a/packages/SettingsLib/SpaPrivileged/OWNERS b/packages/SettingsLib/SpaPrivileged/OWNERS new file mode 100644 index 000000000000..9256ca5cc2b0 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/packages/SettingsLib/Spa/OWNERS diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt new file mode 100644 index 000000000000..a6378ef53437 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt @@ -0,0 +1,58 @@ +/* + * 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.settingslib.spaprivileged.framework.app + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.graphics.drawable.Drawable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.produceState +import com.android.settingslib.Utils +import com.android.settingslib.spa.framework.compose.rememberContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@Composable +fun rememberAppRepository(): AppRepository = rememberContext(::AppRepositoryImpl) + +interface AppRepository { + @Composable + fun produceLabel(app: ApplicationInfo): State<String> + + @Composable + fun produceIcon(app: ApplicationInfo): State<Drawable?> +} + +private class AppRepositoryImpl(private val context: Context) : AppRepository { + private val packageManager = context.packageManager + + @Composable + override fun produceLabel(app: ApplicationInfo) = produceState(initialValue = "", app) { + withContext(Dispatchers.Default) { + value = app.loadLabel(packageManager).toString() + } + } + + @Composable + override fun produceIcon(app: ApplicationInfo) = + produceState<Drawable?>(initialValue = null, app) { + withContext(Dispatchers.Default) { + value = Utils.getBadgedIcon(context, app) + } + } +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt new file mode 100644 index 000000000000..5a3e66619c39 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt @@ -0,0 +1,25 @@ +/* + * 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.settingslib.spaprivileged.framework.app + +import android.content.pm.PackageInfo +import android.content.pm.PackageManager + +object PackageManagers { + fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo = + PackageManager.getPackageInfoAsUserCached(packageName, 0, userId) +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt new file mode 100644 index 000000000000..5ae514cfb524 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt @@ -0,0 +1,70 @@ +/* + * 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.settingslib.spaprivileged.template.app + +import android.content.pm.ApplicationInfo +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.android.settingslib.spa.framework.compose.rememberDrawablePainter +import com.android.settingslib.spa.widget.ui.SettingsBody +import com.android.settingslib.spa.widget.ui.SettingsTitle +import com.android.settingslib.spaprivileged.framework.app.PackageManagers +import com.android.settingslib.spaprivileged.framework.app.rememberAppRepository + +@Composable +fun AppInfo(packageName: String, userId: Int) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally) { + val packageInfo = remember { PackageManagers.getPackageInfoAsUser(packageName, userId) } + Box(modifier = Modifier.padding(8.dp)) { + AppIcon(app = packageInfo.applicationInfo, size = 48) + } + AppLabel(packageInfo.applicationInfo) + Spacer(modifier = Modifier.height(4.dp)) + SettingsBody(packageInfo.versionName) + } +} + +@Composable +fun AppIcon(app: ApplicationInfo, size: Int) { + val appRepository = rememberAppRepository() + Image( + painter = rememberDrawablePainter(appRepository.produceIcon(app).value), + contentDescription = null, + modifier = Modifier.size(size.dp) + ) +} + +@Composable +fun AppLabel(app: ApplicationInfo) { + val appRepository = rememberAppRepository() + SettingsTitle(appRepository.produceLabel(app)) +} diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml index 1de76684d860..a7e44d3f33f5 100644 --- a/packages/SettingsLib/res/values-af/arrays.xml +++ b/packages/SettingsLib/res/values-af/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Gebruik stelselkeuse (verstek)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Gebruik stelselkeuse (verstek)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Gebruik stelselkeuse (verstek)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index fe8030f9a68a..0b23dd130634 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luggehalte"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Uitsaai-inligting"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Huiskontroles"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Kies \'n profielprent"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Verstekgebruikerikoon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fisieke sleutelbord"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index d3c034fa170c..2bf9cb2894e0 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"የአየር ሁኔታ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"የአየር ጥራት"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"የCast መረጃ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"የቤት ውስጥ ቁጥጥሮች"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"የመገለጫ ሥዕል ይምረጡ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ነባሪ የተጠቃሚ አዶ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6783654c32e5..6c71d9c232c4 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"الطقس"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"جودة الهواء"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"معلومات البث"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"إدارة آلية للمنزل"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"اختيار صورة الملف الشخصي"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"رمز المستخدم التلقائي"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 543c703e9003..299df7703e9e 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -121,7 +121,7 @@ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰাৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string> - <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিম প্ৰৱেশ"</string> + <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"এইচ্ছডি অডিঅ’"</string> <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"শ্ৰৱণ যন্ত্ৰ"</string> @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"বতৰ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"বায়ুৰ গুণগত মান"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাষ্টৰ তথ্য"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"গৃহ নিয়ন্ত্ৰণ"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string> diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml index 48974a7bd40f..d1f157af8194 100644 --- a/packages/SettingsLib/res/values-az/arrays.xml +++ b/packages/SettingsLib/res/values-az/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Sistem Seçimini istifadə edin (Defolt)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Sistem Seçimini istifadə edin (Defolt)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Sistem Seçimini istifadə edin (Defolt)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index f3e5d95219fa..fd791924ed32 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Hava"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havanın keyfiyyəti"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayım məlumatı"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Əsas səhifə kontrolları"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Profil şəkli seçin"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Defolt istifadəçi ikonası"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziki klaviatura"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml index 337da265711c..63b08fa92963 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Koristi izbor sistema (podrazumevano)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Koristi izbor sistema (podrazumevano)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Koristi izbor sistema (podrazumevano)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 6a880aac349f..f7d9be91c874 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet vazduha"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o prebacivanju"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Upravljanje domom"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Podrazumevana ikona korisnika"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index a90242667b56..431bcd5bd91a 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Надвор\'е"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якасць паветра"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Даныя пра трансляцыю"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Кіраванне домам"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Выберыце відарыс профілю"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Стандартны карыстальніцкі значок"</string> diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml index 1aad6ca714c4..849e6942100a 100644 --- a/packages/SettingsLib/res/values-bg/arrays.xml +++ b/packages/SettingsLib/res/values-bg/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Използване на сист. избор (стандартно)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"Разширено аудиокодиране (AAC)"</item> + <item msgid="1049450003868150455">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item> + <item msgid="2908219194098827570">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Използване на сист. избор (стандартно)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"Разширено аудиокодиране (AAC)"</item> + <item msgid="8627333814413492563">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item> + <item msgid="3517061573669307965">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Използване на сист. избор (стандартно)"</item> <item msgid="8003118270854840095">"44,1 кХц"</item> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index c5123667f2b2..cbc432207941 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Времето"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество на въздуха"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Предаване: Инф."</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за дома"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Изберете снимка на потребителския профил"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Икона за основния потребител"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Физическа клавиатура"</string> diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index 5e6bb9527c29..a3bc4fd61465 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item> <item msgid="8003118270854840095">"৪৪.১ kHz"</item> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 23eed04b4fb6..3394eea7e80d 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"এয়ার কোয়ালিটি"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাস্ট সম্পর্কিত তথ্য"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"হোম কন্ট্রোল"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"একটি প্রোফাইল ছবি বেছে নিন"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ডিফল্ট ব্যবহারকারীর আইকন"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ফিজিক্যাল কীবোর্ড"</string> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 262a35feab65..926ad8464ccf 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Korištenje odabira sistema (zadano)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Korištenje odabira sistema (zadano)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Korištenje odabira sistema (zadano)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 78a484f28e56..1547e8352abf 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -265,7 +265,7 @@ <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, otklanjanje grešaka, programer"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Prečica za izvještaj o greškama"</string> <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Vidite dugme za prijavu grešaka u meniju napajanja"</string> - <string name="keep_screen_on" msgid="1187161672348797558">"Ne zaključavaj ekran"</string> + <string name="keep_screen_on" msgid="1187161672348797558">"Ne zaključavaj"</string> <string name="keep_screen_on_summary" msgid="1510731514101925829">"Ekran neće prelaziti u stanje mirovanja tokom punjenja"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Omogući Bluetooth HCI snoop zapis"</string> <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Snimite Bluetooth pakete. (Uključite/isključite Bluetooth nakon što promijenite ovu postavku)"</string> @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet zraka"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o emitiranju"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrole doma"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Zadana ikona korisnika"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string> diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml index 8c34a1f0f72a..b6f15903ecbd 100644 --- a/packages/SettingsLib/res/values-ca/arrays.xml +++ b/packages/SettingsLib/res/values-ca/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Utilitza la selecció del sistema (predeterminada)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Utilitza la selecció del sistema (predeterminada)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Utilitza la selecció del sistema (predeterminada)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 606a379b1651..c97a6c1bc157 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Temps"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualitat de l\'aire"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informació d\'emissió"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domòtica"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"D\'una ullada"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Tria una foto de perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icona d\'usuari predeterminat"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclat físic"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 37c0bb4ced79..dfd6d612fb32 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Počasí"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info o odesílání"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládání domácnosti"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Vyberte profilový obrázek"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Výchozí uživatelská ikona"</string> diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml index 155104ae81dc..48a33f61a427 100644 --- a/packages/SettingsLib/res/values-da/arrays.xml +++ b/packages/SettingsLib/res/values-da/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Brug systemvalg (standard)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Brug systemvalg (standard)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Brug systemvalg (standard)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index d80e1ec4cbba..790819ce369f 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Vejr"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast-oplysninger"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Styring af hjem"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Overblik"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Vælg et profilbillede"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon for standardbruger"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string> diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml index 31126a803f8f..ca999db843b5 100644 --- a/packages/SettingsLib/res/values-de/arrays.xml +++ b/packages/SettingsLib/res/values-de/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Systemauswahl verwenden (Standard)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Systemauswahl verwenden (Standard)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Systemauswahl verwenden (Standard)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index d4fafe12e10c..bd919f4d2e37 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Wetter"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftqualität"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Streaming-Info"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Smart-Home-Steuerung"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Profilbild auswählen"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Standardmäßiges Nutzersymbol"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physische Tastatur"</string> diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml index 70000e1917a7..b95f6fc903a7 100644 --- a/packages/SettingsLib/res/values-el/arrays.xml +++ b/packages/SettingsLib/res/values-el/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index aa613a5eeb17..9326dace365a 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Καιρός"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ποιότητα αέρα"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Πληροφορίες ηθοποιών"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Οικιακοί έλεγχοι"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Επιλογή φωτογραφίας προφίλ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Προεπιλεγμένο εικονίδιο χρήστη"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Φυσικό πληκτρολόγιο"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml index fc6f791bf6ea..327e4e9990d1 100644 --- a/packages/SettingsLib/res/values-en-rAU/arrays.xml +++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Use system selection (default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Use system selection (default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Use system selection (default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 62c8e21bf489..667e06a8f832 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml index fc6f791bf6ea..327e4e9990d1 100644 --- a/packages/SettingsLib/res/values-en-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Use system selection (default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Use system selection (default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Use system selection (default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 8650b77dec89..bf20fccb96ce 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index fc6f791bf6ea..327e4e9990d1 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Use system selection (default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Use system selection (default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Use system selection (default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 62c8e21bf489..667e06a8f832 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml index fc6f791bf6ea..327e4e9990d1 100644 --- a/packages/SettingsLib/res/values-en-rIN/arrays.xml +++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Use system selection (default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Use system selection (default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Use system selection (default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 62c8e21bf489..667e06a8f832 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml index 34db380b0cf6..8af0a4ab5903 100644 --- a/packages/SettingsLib/res/values-en-rXC/arrays.xml +++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Use System Selection (Default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Use System Selection (Default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Use System Selection (Default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 21697b9a4fb5..21c1bb72501e 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air Quality"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast Info"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 27cd0ab200f5..8821f424dece 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -661,6 +661,8 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info de reparto"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Control de la casa"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> + <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Elige una foto de perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ícono de usuario predeterminado"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index 0677864d1f46..49244078105e 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Usar preferencia del sistema (predeterminado)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Usar preferencia del sistema (predeterminado)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Usar preferencia del sistema (predeterminado)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index d221c8dbe250..cea4835df66f 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Tiempo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de emisión"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domótica"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"De un vistazo"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Elige una imagen de perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icono de usuario predeterminado"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml index d986ecf47260..0402ac2f5719 100644 --- a/packages/SettingsLib/res/values-et/arrays.xml +++ b/packages/SettingsLib/res/values-et/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Süsteemi valiku kasutamine (vaikeseade)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Süsteemi valiku kasutamine (vaikeseade)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Süsteemi valiku kasutamine (vaikeseade)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index f32ae2347ef6..13fd3190aac8 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Ilm"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Õhukvaliteet"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Osatäitjate teave"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kodu juhtimine"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Ülevaade"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Valige profiilipilt"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Vaikekasutajaikoon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Füüsiline klaviatuur"</string> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index d166e1b97a38..9c12e95cf5c8 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Erabili sistema-hautapena (lehenetsia)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Erabili sistema-hautapena (lehenetsia)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Erabili sistema-hautapena (lehenetsia)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index bcb57e89b213..3c96b6f71c5c 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Eguraldia"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Airearen kalitatea"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Igorpenari buruzko informazioa"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Etxeko gailuak kontrolatzeko aukerak"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Aukeratu profileko argazki bat"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Erabiltzaile lehenetsiaren ikonoa"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teklatu fisikoa"</string> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index b7761dd6ebcb..41410cbc3288 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"استفاده از انتخاب سیستم (پیشفرض)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"استفاده از انتخاب سیستم (پیشفرض)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"استفاده از انتخاب سیستم (پیشفرض)"</item> <item msgid="8003118270854840095">"۴۴٫۱ کیلوهرتز"</item> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 6abe873d27eb..5190e0fbd4ca 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"آبوهوا"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"کیفیت هوا"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"اطلاعات پخش محتوا"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"کنترل لوازم خانگی"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"انتخاب عکس نمایه"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"نماد کاربر پیشفرض"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"صفحهکلید فیزیکی"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 2f3436e1e7c6..c50a2e0139f2 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Sää"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ilmanlaatu"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Striimaustiedot"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kodin ohjaus"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Valitse profiilikuva"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Oletuskäyttäjäkuvake"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index 12acbb6736b4..808c3f2c9a73 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Utiliser sélect. du système (par défaut)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Utiliser sélect. du système (par défaut)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Utiliser sélect. du système (par défaut)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 28b3cd96aed6..b8b465948905 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info diffusion"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domotique"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Aperçu"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choisir une photo de profil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icône d\'utilisateur par défaut"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string> diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml index 80ac7e4e4752..92546da3f56d 100644 --- a/packages/SettingsLib/res/values-fr/arrays.xml +++ b/packages/SettingsLib/res/values-fr/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Utiliser la sélection du système (par défaut)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Utiliser la sélection du système (par défaut)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Utiliser la sélection du système (par défaut)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index da1497aa980b..4a2d01eabb50 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Infos distribution"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domotique"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Choisissez une photo de profil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icône de l\'utilisateur par défaut"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string> diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml index b6cf48e54f72..f66312084e4b 100644 --- a/packages/SettingsLib/res/values-gl/arrays.xml +++ b/packages/SettingsLib/res/values-gl/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Usar selección do sistema (predeterminado)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Usa a selección do sistema (predeterminado)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Usar selección do sistema (predeterminado)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 7822749eea0f..93f9fcf9a296 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -662,6 +662,7 @@ <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Datos da emisión"</string> <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> <skip /> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Espazo intelixente"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Escolle unha imaxe do perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icona do usuario predeterminado"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index 7e668e71f91d..e527d81fbd12 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index cebb1febc001..0e1912558f89 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"હવામાન"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"હવાની ક્વૉલિટી"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"કાસ્ટ વિશેની માહિતી"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ઘરેલુ સાધન નિયંત્રણો"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"પ્રોફાઇલ ફોટો પસંદ કરો"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ભૌતિક કીબોર્ડ"</string> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 13da75b9bdba..9b8d83e67e2e 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"सिस्टम से चुने जाने का इस्तेमाल करें (डिफ़ॉल्ट)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 417d2b282821..ed0a7716987e 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवा की क्वालिटी"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टिंग की जानकारी"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"स्मार्टस्पेस"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफ़ाइल फ़ोटो चुनें"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 267000ff9d4b..af26040bdc52 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvaliteta zraka"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Inform. o emitiranju"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Upravlj. kuć. uređ."</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Odabir profilne slike"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona zadanog korisnika"</string> diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml index a5f37ea4cab9..409d600562f5 100644 --- a/packages/SettingsLib/res/values-hu/arrays.xml +++ b/packages/SettingsLib/res/values-hu/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Rendszerérték (alapértelmezett)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Rendszerérték (alapértelmezett)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Rendszerérték (alapértelmezett)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 69ddad82c712..3db43e739252 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Időjárás"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Levegőminőség"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Átküldési információ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Otthonvezérlés"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Profilkép választása"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Alapértelmezett felhasználó ikonja"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizikai billentyűzet"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index d8efbd36bbeb..8971fceb2cae 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Եղանակ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Օդի որակը"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Հեռարձակման տվյալներ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Տան կարգավորումներ"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Պրոֆիլի նկար ընտրեք"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Օգտատիրոջ կանխադրված պատկերակ"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 5b0ad98aa2d5..95274177e888 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Gunakan Pilihan Sistem (Default)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 029ad8bcb196..6185fd320b81 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualitas Udara"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info Transmisi"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrol Rumah"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Pilih foto profil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna default"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Keyboard fisik"</string> diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index 9d481f8993d8..0b5b9788f96c 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Nota val kerfisins (sjálfgefið)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Nota val kerfisins (sjálfgefið)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Nota val kerfisins (sjálfgefið)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index ccf06b027213..e71878654b5f 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Veður"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Loftgæði"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Útsendingaruppl."</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Heimastýringar"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Veldu prófílmynd"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Tákn sjálfgefins notanda"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Vélbúnaðarlyklaborð"</string> diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml index 62450da37ff7..ae1e515ed573 100644 --- a/packages/SettingsLib/res/values-it/arrays.xml +++ b/packages/SettingsLib/res/values-it/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Usa selezione di sistema (predefinita)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Usa selezione di sistema (predefinita)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Usa selezione di sistema (predefinita)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 3811152467f3..2e9b20205c9b 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -117,7 +117,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivis. contatti e cronologia chiamate"</string> + <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivisione contatti e cronologia chiamate"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usa per condivisione di contatti e cronologia chiamate"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string> @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualità dell\'aria"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info sul cast"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Controlli della casa"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Scegli un\'immagine del profilo"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icona dell\'utente predefinito"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fisica"</string> diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index 49f3fcf3cce6..5d72aff0c04c 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"שימוש בבחירת המערכת (ברירת המחדל)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"שימוש בבחירת המערכת (ברירת המחדל)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"שימוש בבחירת המערכת (ברירת המחדל)"</item> <item msgid="8003118270854840095">"44.1 קילו-הרץ"</item> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 3421fb5e7b83..7e9fd893442c 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"איכות האוויר"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"פרטי ההעברה"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"בית חכם"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"בחירה של תמונת פרופיל"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"סמל המשתמש שמוגדר כברירת מחדל"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"מקלדת פיזית"</string> diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index d73cc4362a56..775e31c11278 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"システムの選択(デフォルト)を使用"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"システムの選択(デフォルト)を使用"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"システムの選択(デフォルト)を使用"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index e7d2cf8cccff..a3311f54d15a 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"天気"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"大気質"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"キャスト情報"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"スマートホーム"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"プロフィール写真の選択"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"デフォルト ユーザー アイコン"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"物理キーボード"</string> diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml index c0d6f97eb552..f3545b6f95d8 100644 --- a/packages/SettingsLib/res/values-ka/arrays.xml +++ b/packages/SettingsLib/res/values-ka/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item> <item msgid="8003118270854840095">"44,1 კჰც"</item> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 09f547132420..b3e4402df582 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ჰაერის ხარისხი"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ტრანსლირების ინფო"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"სახლის მართვა"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"ჭკვიანი სივრცე"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"აირჩიეთ პროფილის სურათი"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"მომხმარებლის ნაგულისხმევი ხატულა"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ფიზიკური კლავიატურა"</string> diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml index fc998e73a16c..9971f8651c12 100644 --- a/packages/SettingsLib/res/values-kk/arrays.xml +++ b/packages/SettingsLib/res/values-kk/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Жүйенің таңдағанын алу (әдепкі)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"L34C"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Жүйенің таңдағанын алу (әдепкі)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"L34C"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Жүйенің таңдағанын алу (әдепкі)"</item> <item msgid="8003118270854840095">"44,1 кГц"</item> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 3281303a6d33..5ecba9bad4bd 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Ауа райы"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ауа сапасы"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Трансляция ақпараты"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Үйді басқару элементтері"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Профиль суретін таңдау"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Әдепкі пайдаланушы белгішесі"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Пернетақта"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 87c1aa22ff92..275bcc17a8ee 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"អាកាសធាតុ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"គុណភាពខ្យល់"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ព័ត៌មានអំពីការបញ្ជូន"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ការគ្រប់គ្រងផ្ទះ"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"ជ្រើសរើសរូបភាពកម្រងព័ត៌មាន"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index ab437e8a2d3a..7e9ff120d80d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"ಹವಾಮಾನ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ವಾಯು ಗುಣಮಟ್ಟ"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ಬಿತ್ತರಿಸಿದ ಮಾಹಿತಿ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ಹೋಮ್ ನಿಯಂತ್ರಣಗಳು"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ಡೀಫಾಲ್ಟ್ ಬಳಕೆದಾರರ ಐಕಾನ್"</string> diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml index 7138113c8b74..16b840bd9f5f 100644 --- a/packages/SettingsLib/res/values-ko/arrays.xml +++ b/packages/SettingsLib/res/values-ko/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"시스템 설정 사용(기본)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"시스템 설정 사용(기본)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"시스템 설정 사용(기본)"</item> <item msgid="8003118270854840095">"44.1kHz"</item> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 2b8ab7558266..fa96ad785773 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -385,7 +385,7 @@ <string name="force_msaa" msgid="4081288296137775550">"4x MSAA 강제 사용"</string> <string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES 2.0 앱에서 4x MSAA 사용"</string> <string name="show_non_rect_clip" msgid="7499758654867881817">"사각형이 아닌 클립 작업 디버그"</string> - <string name="track_frame_time" msgid="522674651937771106">"프로필 HWUI 렌더링"</string> + <string name="track_frame_time" msgid="522674651937771106">"HWUI 렌더링 프로파일"</string> <string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU 디버그 레이어 사용 설정"</string> <string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"디버그 앱에 GPU 디버그 레이어 로드 허용"</string> <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"상세 공급업체 로깅 사용 설정"</string> @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"날씨"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"대기 상태"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"전송 정보"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"홈 컨트롤"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"프로필 사진 선택하기"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"기본 사용자 아이콘"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"물리적 키보드"</string> diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml index 40271f71afdb..700aae13822e 100644 --- a/packages/SettingsLib/res/values-ky/arrays.xml +++ b/packages/SettingsLib/res/values-ky/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"карта13"</item> <item msgid="8147982633566548515">"карта14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Система тандаганды колдонуу (демейки)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Система тандаганды колдонуу (демейки)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Система тандаганды колдонуу (демейки)"</item> <item msgid="8003118270854840095">"44,1 кГц"</item> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index e993e3f3049e..9ebd47be01db 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Аба ырайы"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Абанын сапаты"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Тышкы экранга чыгаруу маалыматы"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Үйдү көзөмөлдөө"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Профилдин сүрөтүн тандоо"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Демейки колдонуучунун сүрөтчөсү"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Аппараттык баскычтоп"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index d295932053e2..11d6d43749df 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"ສະພາບອາກາດ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ຄຸນນະພາບອາກາດ"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ຂໍ້ມູນການສົ່ງສັນຍານ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ການຄວບຄຸມເຮືອນ"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"ເລືອກຮູບໂປຣໄຟລ໌"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string> diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml index 946f69c3030b..c0aafdcd4da3 100644 --- a/packages/SettingsLib/res/values-lt/arrays.xml +++ b/packages/SettingsLib/res/values-lt/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Naudoti sistemos pasirink. (numatytasis)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Naudoti sistemos pasirink. (numatytasis)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Naudoti sistemos pasirink. (numatytasis)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index e38889bbf5ad..e016956b96e3 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Oro kokybė"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Perdav. informacija"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Namų sist. valdikl."</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Pasirinkite profilio nuotrauką"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Numatytojo naudotojo piktograma"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizinė klaviatūra"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index d899adaa1f0e..ffd416353b2a 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Laikapstākļi"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Gaisa kvalitāte"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Apraides informācija"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Mājas kontrolierīces"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Profila attēla izvēle"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Noklusējuma lietotāja ikona"</string> diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 9c46f216522c..41427c1fc871 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Користи избор на системот (стандардно)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Користи избор на системот (стандардно)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Користи избор на системот (стандардно)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index c8033e3e9ce9..26a87e3cbcd5 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет на воздух"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Инфо за улогите"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за домот"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Изберете профилна слика"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Икона за стандарден корисник"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string> diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index 4715e2a32643..98e3bd6ed75b 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 089f6afd6271..5d9f799b8918 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"വായു നിലവാരം"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"കാസ്റ്റ് വിവരങ്ങൾ"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ഹോം കൺട്രോളുകൾ"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ഫിസിക്കൽ കീബോർഡ്"</string> diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index 63fa53b799d0..f3c10d7af4a4 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item> <item msgid="8003118270854840095">"44.1 кГц"</item> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index cc350536e8d9..df6fa89b3d54 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -447,8 +447,8 @@ <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Дьютераномаль (улаан-ногоон)"</string> <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string> <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string> - <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулах"</string> - <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгөний засвар нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li>&nbsp;Өнгөнүүдийг илүү нарийвчилж харах</li> <li>&nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string> + <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулга"</string> + <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө тохируулга нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:<br/> <ol> <li>&nbsp;Өнгөнүүдийг илүү нарийвчилж харах</li> <li>&nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах</li> </ol>"</string> <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string> @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Агаарын чанар"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Дамжуулах мэдээлэл"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Гэрийн удирдлага"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Профайл зураг сонгох"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Биет гар"</string> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index a54f99023a42..c37baaa2d0a7 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"सिस्टीम निवड वापरा (डीफॉल्ट)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"सिस्टम निवड वापरा (डीफॉल्ट)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"सिस्टम निवड वापरा (डीफॉल्ट)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 1907d009b896..45975497c49b 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवेची गुणवत्ता"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसंबंधित माहिती"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"स्मार्टस्पेस"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो निवडा"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"डीफॉल्ट वापरकर्ता आयकन"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"वास्तविक कीबोर्ड"</string> diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml index b26ed2317b5f..b19f0380a618 100644 --- a/packages/SettingsLib/res/values-ms/arrays.xml +++ b/packages/SettingsLib/res/values-ms/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Lalai)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Lalai)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Gunakan Pilihan Sistem (Lalai)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index b3929c912e23..14710400bf3f 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualiti Udara"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maklumat Pelakon"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kawalan Rumah"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Pilih gambar profil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna lalai"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Papan kekunci fizikal"</string> diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml index ed95dfe2e008..3398c5bc0e75 100644 --- a/packages/SettingsLib/res/values-my/arrays.xml +++ b/packages/SettingsLib/res/values-my/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item> <item msgid="8003118270854840095">"၄၄.၁ kHz"</item> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index f7a33a95abf7..ee5601f2368a 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"မိုးလေဝသ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"လေထုအရည်အသွေး"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ကာစ် အချက်အလက်"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"အိမ်သတ်မှတ်ချက်များ"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"ပရိုဖိုင်ပုံ ရွေးပါ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ပကတိ ကီးဘုတ်"</string> diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index 317c2dbbce04..7e65fa0d42df 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Bruk systemvalg (standard)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Bruk systemvalg (standard)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Bruk systemvalg (standard)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 9b99631aa738..a8fd256899a8 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Vær"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformasjon"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Hjemkontroller"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Velg et profilbilde"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Standard brukerikon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index b3c3ee2fc86f..ac1f187f12fd 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item> <item msgid="8003118270854840095">"४४.१ kHz"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 1534cba6e1bd..17b81b26606f 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"वायुको गुणस्तर"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसम्बन्धी जानकारी"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"घरायसी उपकरणका नियन्त्रण"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो छान्नुहोस्"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"प्रयोगकर्ताको डिफल्ट आइकन"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"भौतिक किबोर्ड"</string> diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index e8094521fcf7..7c90eabb1123 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Systeemselectie gebruiken (standaard)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Gebruik systeemselectie (standaard)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Systeemselectie gebruiken (standaard)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index ba11b4653b1c..1251ed959059 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luchtkwaliteit"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformatie"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Bediening voor in huis"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Kies een profielfoto"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Standaard gebruikersicoon"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiek toetsenbord"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 09fa59d60ba8..715f1ebca258 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -116,12 +116,12 @@ <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ଫୋନ୍ କଲ୍ଗୁଡ଼ିକ"</string> <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍ପୁଟ୍ ଡିଭାଇସ୍"</string> - <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍"</string> + <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟରନେଟ ଆକ୍ସେସ"</string> <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string> - <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ୍"</string> + <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ଅଡିଓ"</string> <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> @@ -156,7 +156,7 @@ <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପେୟାରିଙ୍ଗ ପାଇଁ ପ୍ରତ୍ୟାଖ୍ୟାନ କରିଦିଆଗଲା।"</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"କମ୍ପ୍ୟୁଟର୍"</string> <string name="bluetooth_talkback_headset" msgid="3406852564400882682">"ହେଡ୍ସେଟ୍"</string> - <string name="bluetooth_talkback_phone" msgid="868393783858123880">"ଫୋନ୍"</string> + <string name="bluetooth_talkback_phone" msgid="868393783858123880">"ଫୋନ"</string> <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ଇମେଜିଙ୍ଗ"</string> <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ହେଡ୍ଫୋନ୍"</string> <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ଇନ୍ପୁଟ୍ ଉପକରଣ"</string> @@ -246,7 +246,7 @@ <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ଛଅ ଡିଜିଟ୍ କୋଡ୍ ବ୍ୟବହାର କରି ନୂଆ ଡିଭାଇସଗୁଡ଼ିକୁ ପେୟାର୍ କରନ୍ତୁ"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"ପେୟାର୍ ହୋଇଥିବା ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"ବର୍ତ୍ତମାନ ସଂଯୁକ୍ତ ଅଛି"</string> - <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"ଡିଭାଇସ୍ ବିବରଣୀ"</string> + <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"ଡିଭାଇସର ବିବରଣୀ"</string> <string name="adb_device_forget" msgid="193072400783068417">"ଭୁଲିଯାଆନ୍ତୁ"</string> <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ଡିଭାଇସ୍ ଫିଙ୍ଗରପ୍ରିଣ୍ଟ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"ସଂଯୋଗ ବିଫଳ ହେଲା"</string> @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"ପାଣିପାଗ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ବାୟୁର ଗୁଣବତ୍ତା"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"କାଷ୍ଟ ସୂଚନା"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ହୋମ କଣ୍ଟ୍ରୋଲ"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ଡିଫଲ୍ଟ ଉପଯୋଗକର୍ତ୍ତା ଆଇକନ"</string> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index a3fae22d9439..0fd5c56a4e7d 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 9df0fefd7ff8..51bf22d5acbb 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"ਮੌਸਮ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ਹਵਾ ਦੀ ਕੁਆਲਿਟੀ"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ਕਾਸਟ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ਹੋਮ ਕੰਟਰੋਲ"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"ਕੋਈ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਚੁਣੋ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਰਤੋਂਕਾਰ ਪ੍ਰਤੀਕ"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index f4599fdf806a..64cbe78d7518 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Pogoda"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Jakość powietrza"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Obsada"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Sterowanie domem"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Wybierz zdjęcie profilowe"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona domyślnego użytkownika"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 1883ef355776..f218fab6508b 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 12507c5449df..531bc6308abe 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -208,7 +208,7 @@ <string name="tts_engine_settings_title" msgid="7849477533103566291">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string> <string name="tts_engine_settings_button" msgid="477155276199968948">"Iniciar configurações do mecanismo"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mecanismo preferencial"</string> - <string name="tts_general_section_title" msgid="8919671529502364567">"Gerais"</string> + <string name="tts_general_section_title" msgid="8919671529502364567">"Geral"</string> <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Redefinir o tom de voz"</string> <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Redefinir o tom de voz para o padrão."</string> <string-array name="tts_rate_entries"> @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Automação residencial"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index 985bd515acd0..e323455b9ce2 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Utilizar seleção do sistema (predefinido)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Utilizar seleção do sistema (predefinido)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Utilizar seleção do sistema (predefinido)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 4988136ba43a..ed3f642e2cc3 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ctr. domésticos"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Espaço inteligente"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Escolha uma imagem do perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone do utilizador predefinido"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 1883ef355776..f218fab6508b 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 12507c5449df..531bc6308abe 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -208,7 +208,7 @@ <string name="tts_engine_settings_title" msgid="7849477533103566291">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string> <string name="tts_engine_settings_button" msgid="477155276199968948">"Iniciar configurações do mecanismo"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mecanismo preferencial"</string> - <string name="tts_general_section_title" msgid="8919671529502364567">"Gerais"</string> + <string name="tts_general_section_title" msgid="8919671529502364567">"Geral"</string> <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Redefinir o tom de voz"</string> <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Redefinir o tom de voz para o padrão."</string> <string-array name="tts_rate_entries"> @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Automação residencial"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string> diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index f1e99865c9a6..34b0ac9e2500 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Folosiți selectarea sistemului (prestabilit)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Folosiți selectarea sistemului (prestabilit)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Folosiți selectarea sistemului (prestabilit)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index f49fcdd96a05..8f87a7c10f97 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calitatea aerului"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informații artiști"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Controlul locuinței"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Alegeți o fotografie de profil"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Pictograma prestabilită a utilizatorului"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastatură fizică"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 38930a5117ca..e2f05dc6e349 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество воздуха"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Данные о трансляции"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Автоматизация дома"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Выберите фото профиля"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Значок пользователя по умолчанию"</string> diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml index 8386c1aa5559..eaacfb835de5 100644 --- a/packages/SettingsLib/res/values-si/arrays.xml +++ b/packages/SettingsLib/res/values-si/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්රව්යය"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්රව්යය"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්රව්යය"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්රව්යය"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index fa0296f80ff0..1d52e0b5b9f7 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"කාලගුණය"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"වායු ගුණත්වය"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"විකාශ තතු"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"නිවෙස් පාලන"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"ස්මාර්ට් අවකාශය"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"පැතිකඩ පින්තූරයක් තේරීම"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"පෙරනිමි පරිශීලක නිරූපකය"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"භෞතික යතුරු පුවරුව"</string> diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml index 370b23f6cd58..bbfe9696e698 100644 --- a/packages/SettingsLib/res/values-sk/arrays.xml +++ b/packages/SettingsLib/res/values-sk/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Použiť voľbu systému (predvolené)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Použiť voľbu systému (predvolené)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Použiť voľbu systému (predvolené)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index a7c69c8247d8..cf501cd62d17 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informácie o prenose"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládanie domácnosti"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Výber profilovej fotky"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Predvolená ikona používateľa"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnica"</string> diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index 6e33e386d897..b2003e5efbc6 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Uporabi sistemsko izbiro (privzeto)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Uporabi sistemsko izbiro (privzeto)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Uporabi sistemsko izbiro (privzeto)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 7334ceafc954..bc89f5af3570 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kakovost zraka"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"O zasedbi"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Nadzor doma"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Hitri pregled"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Izbira profilne slike"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Privzeta ikona uporabnika"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fizična tipkovnica"</string> diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml index 8a6d853e7818..ed8638016c34 100644 --- a/packages/SettingsLib/res/values-sq/arrays.xml +++ b/packages/SettingsLib/res/values-sq/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 59359cdf74fd..e7af25d7142f 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Moti"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Cilësia e ajrit"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Të dhënat e aktorëve"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrollet e shtëpisë"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Zgjidh një fotografi profili"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikona e parazgjedhur e përdoruesit"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fizike"</string> diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml index 7e198bfe3f6d..a95e47b6b2b0 100644 --- a/packages/SettingsLib/res/values-sr/arrays.xml +++ b/packages/SettingsLib/res/values-sr/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Користи избор система (подразумевано)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Користи избор система (подразумевано)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Користи избор система (подразумевано)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index eee3f8917c50..a02d674b92a0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Време"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет ваздуха"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Подаци о пребацивању"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Управљање домом"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Одаберите слику профила"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Подразумевана икона корисника"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string> diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index f99a85b84533..c63465c65d83 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Använd systemval (standardinställning)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Använd systemval (standardinställning)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Använd systemval (standardinställning)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 1a8815c1164b..e135d1b47859 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Väder"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info om rollistan"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Hemstyrning"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Välj en profilbild"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Ikon för standardanvändare"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiskt tangentbord"</string> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index dab4279f88dc..53dc6e5440ad 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"ramani ya 13"</item> <item msgid="8147982633566548515">"ramani ya 14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item> <item msgid="8003118270854840095">"kHz 44.1"</item> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 5f1d141a0256..a900064efc29 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Hali ya Hewa"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ubora wa Hewa"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maelezo ya Wahusika"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Udhibiti wa Vifaa Nyumbani"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Chagua picha ya wasifu"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Aikoni chaguomsingi ya mtumiaji"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Kibodi halisi"</string> diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml index a0f1fa6447c8..957bd61130f3 100644 --- a/packages/SettingsLib/res/values-ta/arrays.xml +++ b/packages/SettingsLib/res/values-ta/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index b8c37b037d02..645278c8fd87 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"வானிலை"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"காற்றின் தரம்"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"அலைபரப்புத் தகவல்"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ஹோம் கன்ட்ரோல்கள்"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"சுயவிவரப் படத்தைத் தேர்வுசெய்யுங்கள்"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"இயல்புநிலைப் பயனர் ஐகான்"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"கீபோர்டு"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 18a27585cc8c..d4361e52232f 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 504c827e9a9e..de3f390cda19 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"గాలి క్వాలిటీ"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"కాస్ట్ సమాచారం"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"హోమ్ కంట్రోల్స్"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"స్మార్ట్స్పేస్"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"ప్రొఫైల్ ఫోటోను ఎంచుకోండి"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ఆటోమేటిక్ సెట్టింగ్ యూజర్ చిహ్నం"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"భౌతిక కీబోర్డ్"</string> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 04a5f4dd86b9..782e95e91cfe 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index f33630dd0ea4..dfb87a8d19af 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"สภาพอากาศ"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"คุณภาพอากาศ"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ข้อมูลแคสต์"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ระบบควบคุมอุปกรณ์ในบ้าน"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"เลือกรูปโปรไฟล์"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ไอคอนผู้ใช้เริ่มต้น"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"แป้นพิมพ์จริง"</string> diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml index 59cb1f370338..19d3423a7b48 100644 --- a/packages/SettingsLib/res/values-tl/arrays.xml +++ b/packages/SettingsLib/res/values-tl/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Gamitin ang Pagpili ng System (Default)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Gamitin ang Pagpili ng System (Default)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Gamitin ang Pagpili ng System (Default)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index f95f0e50f1be..fdaece106798 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Lagay ng Panahon"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kalidad ng Hangin"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Impormasyon ng Cast"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Pumili ng larawan sa profile"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Icon ng default na user"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Pisikal na keyboard"</string> diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index 5ed35faee347..37891ae5cf3b 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Sistem Seçimini Kullan (Varsayılan)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Sistem Seçimini Kullan (Varsayılan)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Sistem Seçimini Kullan (Varsayılan)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 2fa2bd18c5f3..a2eb0e2bd030 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Hava durumu"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Hava Kalitesi"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayın Bilgisi"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ev Kontrolleri"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Profil fotoğrafı seçin"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Varsayılan kullanıcı simgesi"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziksel klavye"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 9be0855b1d48..63d711808748 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якість повітря"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Акторський склад"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Автоматизація дому"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Виберіть зображення профілю"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Значок користувача за умовчанням"</string> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index d0974580b5ee..db9941e7d436 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 8eb9a117e1a3..2de47ecb0cf2 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -661,6 +661,7 @@ <string name="dream_complication_title_aqi" msgid="4587552608957834110">"ہوا کا معیار"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"کاسٹ کرنے کی معلومات"</string> <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ہوم کنٹرولز"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"پروفائل کی تصویر منتخب کریں"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"ڈیفالٹ صارف کا آئیکن"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"فزیکل کی بورڈ"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index a5c868360eae..707a54edb57f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -660,7 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Ob-havo"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havo sifati"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Translatsiya axboroti"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Uy boshqaruvi"</string> + <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) --> <skip /> <string name="avatar_picker_title" msgid="8492884172713170652">"Profil rasmini tanlash"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Foydalanuvchining standart belgisi"</string> diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index 31867e2f18f7..ee599d64bd39 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="2908219194098827570">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item> + <item msgid="3517061573669307965">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item> <item msgid="8003118270854840095">"44,1 kHz"</item> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 45a0465719ed..82eb63d6ca23 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Thời tiết"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Chất lượng không khí"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Thông tin về dàn nghệ sĩ"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Điều khiển nhà"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Chọn một ảnh hồ sơ"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Biểu tượng người dùng mặc định"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Bàn phím thực"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 973d7d01fcc9..2a85d311a5c4 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"使用系统选择(默认)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"使用系统选择(默认)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"使用系统选择(默认)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 2bc8a305d0fc..9f8007b9cb40 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"天气"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空气质量"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放信息"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"家居控制"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"选择个人资料照片"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"默认用户图标"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml index 87f382524a5c..a84f0e2fa88c 100644 --- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"使用系統選擇 (預設)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"使用系統選擇 (預設)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"使用系統選擇 (預設)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 114bc6424db5..cc80ebcf9e02 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣質素"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放資料"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"智能家居"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人檔案相片"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml index 529287f4354f..66aaa56b201a 100644 --- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"map13"</item> <item msgid="8147982633566548515">"map14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"系統自動選擇 (預設)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item> + <item msgid="3825367753087348007">"LDAC"</item> + <item msgid="328951785723550863">"LC3"</item> + <item msgid="506175145534048710">"Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"系統自動選擇 (預設)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item> + <item msgid="2553206901068987657">"LDAC"</item> + <item msgid="3940992993241040716">"LC3"</item> + <item msgid="7940970833006181407">"Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"系統自動選擇 (預設)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index a7ae213c26ba..46aaef88e757 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣品質"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"演出者資訊"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"居家控制系統"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"智慧空間"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人資料相片"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string> diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml index 59ead86f0f8f..0494f1c0b6df 100644 --- a/packages/SettingsLib/res/values-zu/arrays.xml +++ b/packages/SettingsLib/res/values-zu/arrays.xml @@ -85,10 +85,26 @@ <item msgid="7073042887003102964">"Imephu13"</item> <item msgid="8147982633566548515">"Imephu14"</item> </string-array> - <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) --> - <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) --> - <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) --> + <string-array name="bluetooth_a2dp_codec_titles"> + <item msgid="2494959071796102843">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item> + <item msgid="4055460186095649420">"SBC"</item> + <item msgid="720249083677397051">"I-AAC"</item> + <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item> + <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item> + <item msgid="3825367753087348007">"I-LDAC"</item> + <item msgid="328951785723550863">"I-LC3"</item> + <item msgid="506175145534048710">"I-Opus"</item> + </string-array> + <string-array name="bluetooth_a2dp_codec_summaries"> + <item msgid="8868109554557331312">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item> + <item msgid="9024885861221697796">"SBC"</item> + <item msgid="4688890470703790013">"I-AAC"</item> + <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item> + <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item> + <item msgid="2553206901068987657">"I-LDAC"</item> + <item msgid="3940992993241040716">"I-LC3"</item> + <item msgid="7940970833006181407">"I-Opus"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item msgid="926809261293414607">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item> <item msgid="8003118270854840095">"44.1 kHz"</item> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index e037d1275ed1..e78835741bd7 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -660,8 +660,8 @@ <string name="dream_complication_title_weather" msgid="598609151677172783">"Isimo sezulu"</string> <string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ikhwalithi Yomoya"</string> <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Ulwazi Lokusakaza"</string> - <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) --> - <skip /> + <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Izilawuli Zasekhaya"</string> + <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"I-Smartspace"</string> <string name="avatar_picker_title" msgid="8492884172713170652">"Khetha isithombe sephrofayela"</string> <string name="default_user_icon_description" msgid="6554047177298972638">"Isithonjana somsebenzisi sokuzenzakalelayo"</string> <string name="physical_keyboard_title" msgid="4811935435315835220">"Ikhibhodi ephathekayo"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b8792236d0d9..11cb9c1b54c9 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -962,6 +962,9 @@ <!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] --> <string name="enable_freeform_support_summary">Enable support for experimental freeform windows.</string> + <!-- UI debug setting: enable desktop mode [CHAR LIMIT=25] --> + <string name="desktop_mode">Desktop mode</string> + <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] --> <string name="local_backup_password_title">Desktop backup password</string> <!-- Summary text of the "local backup password" setting when the user has not supplied a password --> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index d9262cce3cb9..766c036d521c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -528,7 +528,7 @@ public class InfoMediaManager extends MediaManager { class RouterManagerCallback implements MediaRouter2Manager.Callback { @Override - public void onRoutesAdded(List<MediaRoute2Info> routes) { + public void onRoutesUpdated() { refreshDevices(); } @@ -540,16 +540,6 @@ public class InfoMediaManager extends MediaManager { } @Override - public void onRoutesChanged(List<MediaRoute2Info> routes) { - refreshDevices(); - } - - @Override - public void onRoutesRemoved(List<MediaRoute2Info> routes) { - refreshDevices(); - } - - @Override public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) { if (DEBUG) { Log.d(TAG, "onTransferred() oldSession : " + oldSession.getName() diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index ee7b7d6b180f..f4af6e852580 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -112,7 +112,7 @@ public class InfoMediaManagerTest { final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); assertThat(mediaDevice).isNull(); - mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); @@ -135,7 +135,7 @@ public class InfoMediaManagerTest { assertThat(mediaDevice).isNull(); mInfoMediaManager.mPackageName = ""; - mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); @@ -199,7 +199,7 @@ public class InfoMediaManagerTest { final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); assertThat(mediaDevice).isNull(); - mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); @@ -222,7 +222,7 @@ public class InfoMediaManagerTest { assertThat(mediaDevice).isNull(); mInfoMediaManager.mPackageName = ""; - mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); @@ -263,7 +263,7 @@ public class InfoMediaManagerTest { final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); assertThat(mediaDevice).isNull(); - mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); @@ -286,7 +286,7 @@ public class InfoMediaManagerTest { assertThat(mediaDevice).isNull(); mInfoMediaManager.mPackageName = ""; - mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); assertThat(infoDevice.getId()).isEqualTo(TEST_ID); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 76209da47d55..f0915f8be287 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -337,9 +337,6 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.Wearable.BEDTIME_MODE, BOOLEAN_VALIDATOR); VALIDATORS.put( - Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT, - NON_NEGATIVE_INTEGER_VALIDATOR); - VALIDATORS.put( Global.Wearable.EARLY_UPDATES_STATUS, new DiscreteValueValidator( new String[] { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 874e57069af3..ba7a9bcc7cd7 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -98,6 +98,7 @@ public class SettingsBackupTest { Settings.System.VOLUME_VOICE, // deprecated since API 2? Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only + Settings.System.DESKTOP_MODE, // developer setting for internal prototyping Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities Settings.System.SCREEN_BRIGHTNESS_FLOAT, @@ -656,7 +657,6 @@ public class SettingsBackupTest { Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED, Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED, Settings.Global.Wearable.BEDTIME_MODE, - Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT, Settings.Global.Wearable.EARLY_UPDATES_STATUS); private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS = diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt new file mode 100644 index 000000000000..925fae0ebfb4 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt @@ -0,0 +1,67 @@ +/* + * 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.internal.systemui.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression + +@Suppress("UnstableApiUsage") +class BindServiceViaContextDetector : Detector(), SourceCodeScanner { + + override fun getApplicableMethodNames(): List<String> { + return listOf("bindService", "bindServiceAsUser", "unbindService") + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) { + context.report( + ISSUE, + method, + context.getNameLocation(node), + "Binding or unbinding services are synchronous calls, please make " + + "sure you're on a @Background Executor." + ) + } + } + + companion object { + @JvmField + val ISSUE: Issue = + Issue.create( + id = "BindServiceViaContextDetector", + briefDescription = "Service bound/unbound via Context, please make sure " + + "you're on a background thread.", + explanation = + "Binding or unbinding services are synchronous calls to ActivityManager, " + + "they usually take multiple milliseconds to complete and will make" + + "the caller drop frames. Make sure you're on a @Background Executor.", + category = Category.PERFORMANCE, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(BindServiceViaContextDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + } +} diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt index 397a110f4bc7..226aebbd0464 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -27,7 +27,8 @@ import com.google.auto.service.AutoService class SystemUIIssueRegistry : IssueRegistry() { override val issues: List<Issue> - get() = listOf(BroadcastSentViaContextDetector.ISSUE) + get() = listOf(BindServiceViaContextDetector.ISSUE, + BroadcastSentViaContextDetector.ISSUE) override val api: Int get() = CURRENT_API diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt new file mode 100644 index 000000000000..bf685f7c178e --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt @@ -0,0 +1,140 @@ +/* + * 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.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test + +class BindServiceViaContextDetectorTest : LintDetectorTest() { + + override fun getDetector(): Detector = BindServiceViaContextDetector() + override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + + override fun getIssues(): List<Issue> = listOf( + BindServiceViaContextDetector.ISSUE) + + private val explanation = "Binding or unbinding services are synchronous calls" + + @Test + fun testBindService() { + lint().files( + TestFiles.java( + """ + package test.pkg; + import android.content.Context; + + public class TestClass1 { + public void bind(Context context) { + Intent intent = new Intent(Intent.ACTION_VIEW); + context.bindService(intent, null, 0); + } + } + """ + ).indented(), + *stubs) + .issues(BindServiceViaContextDetector.ISSUE) + .run() + .expectWarningCount(1) + .expectContains(explanation) + } + + @Test + fun testBindServiceAsUser() { + lint().files( + TestFiles.java( + """ + package test.pkg; + import android.content.Context; + import android.os.UserHandle; + + public class TestClass1 { + public void bind(Context context) { + Intent intent = new Intent(Intent.ACTION_VIEW); + context.bindServiceAsUser(intent, null, 0, UserHandle.ALL); + } + } + """ + ).indented(), + *stubs) + .issues(BindServiceViaContextDetector.ISSUE) + .run() + .expectWarningCount(1) + .expectContains(explanation) + } + + @Test + fun testUnbindService() { + lint().files( + TestFiles.java( + """ + package test.pkg; + import android.content.Context; + import android.content.ServiceConnection; + + public class TestClass1 { + public void unbind(Context context, ServiceConnection connection) { + context.unbindService(connection); + } + } + """ + ).indented(), + *stubs) + .issues(BindServiceViaContextDetector.ISSUE) + .run() + .expectWarningCount(1) + .expectContains(explanation) + } + + private val contextStub: TestFile = java( + """ + package android.content; + import android.os.UserHandle; + + public class Context { + public void bindService(Intent intent) {}; + public void bindServiceAsUser(Intent intent, ServiceConnection connection, int flags, + UserHandle userHandle) {}; + public void unbindService(ServiceConnection connection) {}; + } + """ + ) + + private val serviceConnectionStub: TestFile = java( + """ + package android.content; + + public class ServiceConnection {} + """ + ) + + private val userHandleStub: TestFile = java( + """ + package android.os; + + public enum UserHandle { + ALL + } + """ + ) + + private val stubs = arrayOf(contextStub, serviceConnectionStub, userHandleStub) +} diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 1bf30377d7ff..7f3caeca5a62 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -799,7 +799,7 @@ <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] --> <string name="keyguard_unlock">Swipe up to open</string> - <!-- Message shown when lock screen is unlocked (ie: by trust agent) and the user taps the empty space on the lock screen and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> + <!-- Message shown when lock screen is unlocked (ie: by trust agent or face auth). Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> <string name="keyguard_unlock_press">Press the unlock icon to open</string> <!-- Message shown when non-bypass face authentication succeeds. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> @@ -813,6 +813,10 @@ <!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> <string name="keyguard_face_successful_unlock_press_alt_3">Face recognized. Press the unlock icon to open.</string> + <!-- Message shown when non-bypass face authentication succeeds. [CHAR LIMIT=60] --> + <string name="keyguard_face_successful_unlock">Unlocked by face</string> + <!-- Message shown when non-bypass face authentication succeeds. [CHAR LIMIT=60] --> + <string name="keyguard_face_successful_unlock_alt1">Face recognized</string> <!-- Messages shown when users press outside of udfps region during --> <string-array name="udfps_accessibility_touch_hints"> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8b5e3c11b2f6..d27b9ced66a7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -54,6 +54,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -2019,12 +2020,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // in case authenticators aren't registered yet at this point: mAuthController.addCallback(new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { + public void onAllAuthenticatorsRegistered( + @BiometricAuthenticator.Modality int modality) { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } @Override - public void onEnrollmentsChanged() { + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE)); } }); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 99e0ce29a8c2..7a42803859b5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -271,7 +271,7 @@ public class KeyguardUpdateMonitorCallback { * like fingerprint authentication errors. * * @param message Message that indicates an error. - * @see KeyguardIndicationController.BaseKeyguardCallback#HIDE_DELAY_MS + * @see KeyguardIndicationController#DEFAULT_HIDE_DELAY_MS * @see KeyguardIndicationController#showTransientIndication(CharSequence) */ public void onTrustAgentErrorMessage(CharSequence message) { } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 06e1828ef9f4..d6974dfac570 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.LockIconView.ICON_FINGERPRINT; @@ -29,6 +30,7 @@ import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AnimatedStateListDrawable; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricSourceType; import android.os.Process; import android.os.VibrationAttributes; @@ -701,13 +703,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsConfig(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsConfig(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsConfig(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsConfig(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 47ff59cfc281..282f25104c44 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -46,6 +46,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; @@ -156,25 +157,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } }; - private final IFingerprintAuthenticatorsRegisteredCallback - mFingerprintAuthenticatorsRegisteredCallback = - new IFingerprintAuthenticatorsRegisteredCallback.Stub() { - @Override - public void onAllAuthenticatorsRegistered( - List<FingerprintSensorPropertiesInternal> sensors) { - mHandler.post(() -> handleAllFingerprintAuthenticatorsRegistered(sensors)); - } - }; - - private final BiometricStateListener mBiometricStateListener = - new BiometricStateListener() { - @Override - public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { - mHandler.post( - () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments)); - } - }; - @VisibleForTesting final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -249,8 +231,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba List<FingerprintSensorPropertiesInternal> sensors) { mExecution.assertIsMainThread(); if (DEBUG) { - Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString( - sensors.toArray())); + Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: " + + Arrays.toString(sensors.toArray())); } mAllFingerprintAuthenticatorsRegistered = true; mFpProps = sensors; @@ -292,15 +274,42 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba mSidefpsController = mSidefpsControllerFactory.get(); } - mFingerprintManager.registerBiometricStateListener(mBiometricStateListener); + mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mHandler.post(() -> handleEnrollmentsChanged( + TYPE_FINGERPRINT, userId, sensorId, hasEnrollments)); + } + }); updateFingerprintLocation(); for (Callback cb : mCallbacks) { - cb.onAllAuthenticatorsRegistered(); + cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); } } - private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + private void handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) { + mExecution.assertIsMainThread(); + if (DEBUG) { + Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString( + sensors.toArray())); + } + + mFaceManager.registerBiometricStateListener(new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mHandler.post(() -> handleEnrollmentsChanged( + TYPE_FACE, userId, sensorId, hasEnrollments)); + } + }); + + for (Callback cb : mCallbacks) { + cb.onAllAuthenticatorsRegistered(TYPE_FACE); + } + } + + private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId, + boolean hasEnrollments) { mExecution.assertIsMainThread(); Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId + ", hasEnrollments: " + hasEnrollments); @@ -314,7 +323,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } for (Callback cb : mCallbacks) { - cb.onEnrollmentsChanged(); + cb.onEnrollmentsChanged(modality); } } @@ -700,7 +709,26 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba if (mFingerprintManager != null) { mFingerprintManager.addAuthenticatorsRegisteredCallback( - mFingerprintAuthenticatorsRegisteredCallback); + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + mHandler.post(() -> + handleAllFingerprintAuthenticatorsRegistered(sensors)); + } + }); + } + if (mFaceManager != null) { + mFaceManager.addAuthenticatorsRegisteredCallback( + new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FaceSensorPropertiesInternal> sensors) { + mHandler.post(() -> + handleAllFaceAuthenticatorsRegistered(sensors)); + } + } + ); } mStableDisplaySize = mDisplayManager.getStableDisplaySize(); @@ -1116,13 +1144,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. */ - default void onAllAuthenticatorsRegistered() {} + default void onAllAuthenticatorsRegistered(@Modality int modality) {} /** - * Called when UDFPS enrollments have changed. This is called after boot and on changes to + * Called when enrollments have changed. This is called after boot and on changes to * enrollment. */ - default void onEnrollmentsChanged() {} + default void onEnrollmentsChanged(@Modality int modality) {} /** * Called when the biometric prompt starts showing. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 38fab8ffbfad..fd3f6007d8a9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -308,7 +308,7 @@ class AuthRippleController @Inject constructor( private val authControllerCallback = object : AuthController.Callback { - override fun onAllAuthenticatorsRegistered() { + override fun onAllAuthenticatorsRegistered(modality: Int) { updateUdfpsDependentParams() updateSensorLocation() } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index a9e310d25f9c..7da2cf150147 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -16,12 +16,15 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; +import android.hardware.biometrics.BiometricAuthenticator; import android.os.Handler; import android.util.Log; import android.view.Display; @@ -232,13 +235,17 @@ public class DozeScreenState implements DozeMachine.Part { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsController(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsController(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsController(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsController(); + } } }; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index da6c163b1eea..997a6e554364 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY; @@ -29,6 +31,7 @@ import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.display.AmbientDisplayConfiguration; import android.net.Uri; import android.os.Handler; @@ -835,13 +838,17 @@ public class DozeSensors { private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override - public void onAllAuthenticatorsRegistered() { - updateUdfpsEnrolled(); + public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsEnrolled(); + } } @Override - public void onEnrollmentsChanged() { - updateUdfpsEnrolled(); + public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) { + if (modality == TYPE_FINGERPRINT) { + updateUdfpsEnrolled(); + } } private void updateUdfpsEnrolled() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index bd00ce61a7e3..1f52fc6e022c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -27,6 +27,7 @@ import androidx.annotation.IntDef; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -56,8 +57,11 @@ import java.util.Map; public class KeyguardIndicationRotateTextViewController extends ViewController<KeyguardIndicationTextView> implements Dumpable { public static String TAG = "KgIndicationRotatingCtrl"; - private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds - public static final long IMPORTANT_MSG_MIN_DURATION = 2000L + 600L; // 2000ms + [Y in duration] + private static final long DEFAULT_INDICATION_SHOW_LENGTH = + KeyguardIndicationController.DEFAULT_HIDE_DELAY_MS + - KeyguardIndicationTextView.Y_IN_DURATION; + public static final long IMPORTANT_MSG_MIN_DURATION = + 2000L + KeyguardIndicationTextView.Y_IN_DURATION; private final StatusBarStateController mStatusBarStateController; private final float mMaxAlpha; @@ -375,6 +379,7 @@ public class KeyguardIndicationRotateTextViewController extends public static final int INDICATION_TYPE_USER_LOCKED = 8; public static final int INDICATION_TYPE_REVERSE_CHARGING = 10; public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11; + public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12; @IntDef({ INDICATION_TYPE_NONE, @@ -388,7 +393,8 @@ public class KeyguardIndicationRotateTextViewController extends INDICATION_TYPE_RESTING, INDICATION_TYPE_USER_LOCKED, INDICATION_TYPE_REVERSE_CHARGING, - INDICATION_TYPE_BIOMETRIC_MESSAGE + INDICATION_TYPE_BIOMETRIC_MESSAGE, + INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP }) @Retention(RetentionPolicy.SOURCE) public @interface IndicationType{} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt index beb54c85e021..4397d3d85d62 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt @@ -18,7 +18,6 @@ package com.android.systemui.screenshot import android.net.Uri import android.util.Log -import android.view.WindowManager.ScreenshotType import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION @@ -37,13 +36,12 @@ internal class RequestProcessor @Inject constructor( private val controller: ScreenshotController, ) { fun processRequest( - @ScreenshotType type: Int, - onSavedListener: Consumer<Uri>, request: ScreenshotRequest, + onSavedListener: Consumer<Uri>, callback: RequestCallback ) { - if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) { + if (request.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) { val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle) controller.handleImageAsScreenshot( @@ -53,12 +51,12 @@ internal class RequestProcessor @Inject constructor( return } - when (type) { + when (request.type) { TAKE_SCREENSHOT_FULLSCREEN -> controller.takeScreenshotFullscreen(null, onSavedListener, callback) TAKE_SCREENSHOT_SELECTED_REGION -> controller.takeScreenshotPartial(null, onSavedListener, callback) - else -> Log.w(TAG, "Invalid screenshot option: $type") + else -> Log.w(TAG, "Invalid screenshot option: ${request.type}") } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index f1f0223632b7..7bf3217e5f15 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -229,11 +229,11 @@ public class TakeScreenshotService extends Service { if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) { Log.d(TAG, "handleMessage: Using request processor"); - mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback); + mProcessor.processRequest(screenshotRequest, uriConsumer, requestCallback); return true; } - switch (msg.what) { + switch (screenshotRequest.getType()) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: if (DEBUG_SERVICE) { Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index ca147286a301..c98364473f71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -27,6 +27,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; @@ -36,7 +37,6 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; -import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -123,6 +123,8 @@ public class KeyguardIndicationController { private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2; private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3; private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; + public static final long DEFAULT_HIDE_DELAY_MS = + 3500 + KeyguardIndicationTextView.Y_IN_DURATION; private final Context mContext; private final BroadcastDispatcher mBroadcastDispatcher; @@ -140,7 +142,6 @@ public class KeyguardIndicationController { protected final @Main DelayableExecutor mExecutor; protected final @Background DelayableExecutor mBackgroundExecutor; private final LockPatternUtils mLockPatternUtils; - private final IActivityManager mIActivityManager; private final FalsingManager mFalsingManager; private final KeyguardBypassController mKeyguardBypassController; private final AccessibilityManager mAccessibilityManager; @@ -155,6 +156,7 @@ public class KeyguardIndicationController { private CharSequence mTrustGrantedIndication; private CharSequence mTransientIndication; private CharSequence mBiometricMessage; + private CharSequence mBiometricMessageFollowUp; protected ColorStateList mInitialTextColorState; private boolean mVisible; private boolean mOrganizationOwnedDevice; @@ -171,7 +173,7 @@ public class KeyguardIndicationController { private int mBatteryLevel; private boolean mBatteryPresent = true; private long mChargingTimeRemaining; - private String mMessageToShowOnScreenOn; + private String mBiometricErrorMessageToShowOnScreenOn; private final Set<Integer> mCoExFaceHelpMsgIdsToShow; private boolean mInited; @@ -189,11 +191,11 @@ public class KeyguardIndicationController { private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurnedOn() { - if (mMessageToShowOnScreenOn != null) { - showBiometricMessage(mMessageToShowOnScreenOn); + if (mBiometricErrorMessageToShowOnScreenOn != null) { + showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn); // We want to keep this message around in case the screen was off - hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); - mMessageToShowOnScreenOn = null; + hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS); + mBiometricErrorMessageToShowOnScreenOn = null; } } }; @@ -219,7 +221,6 @@ public class KeyguardIndicationController { FalsingManager falsingManager, LockPatternUtils lockPatternUtils, ScreenLifecycle screenLifecycle, - IActivityManager iActivityManager, KeyguardBypassController keyguardBypassController, AccessibilityManager accessibilityManager) { mContext = context; @@ -236,7 +237,6 @@ public class KeyguardIndicationController { mExecutor = executor; mBackgroundExecutor = bgExecutor; mLockPatternUtils = lockPatternUtils; - mIActivityManager = iActivityManager; mFalsingManager = falsingManager; mKeyguardBypassController = keyguardBypassController; mAccessibilityManager = accessibilityManager; @@ -498,8 +498,23 @@ public class KeyguardIndicationController { .build(), true ); + if (!TextUtils.isEmpty(mBiometricMessageFollowUp)) { + mRotateTextViewController.updateIndication( + INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, + new KeyguardIndication.Builder() + .setMessage(mBiometricMessageFollowUp) + .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION) + .setTextColor(mInitialTextColorState) + .build(), + true + ); + } else { + mRotateTextViewController.hideIndication( + INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP); + } } else { mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE); + mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP); } } @@ -719,38 +734,45 @@ public class KeyguardIndicationController { private void showTransientIndication(CharSequence transientIndication) { mTransientIndication = transientIndication; mHandler.removeMessages(MSG_HIDE_TRANSIENT); - hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); + hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS); updateTransient(); } - /** - * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}. - */ - public void showBiometricMessage(int biometricMessage) { - showBiometricMessage(mContext.getResources().getString(biometricMessage)); + private void showBiometricMessage(CharSequence biometricMessage) { + showBiometricMessage(biometricMessage, null); } /** - * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}. + * Shows {@param biometricMessage} and {@param biometricMessageFollowUp} + * until they are hidden by {@link #hideBiometricMessage}. Messages are rotated through + * by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message + * logic. */ - private void showBiometricMessage(CharSequence biometricMessage) { + private void showBiometricMessage(CharSequence biometricMessage, + CharSequence biometricMessageFollowUp) { if (TextUtils.equals(biometricMessage, mBiometricMessage)) { return; } mBiometricMessage = biometricMessage; + mBiometricMessageFollowUp = biometricMessageFollowUp; mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK); mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); - hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); + hideBiometricMessageDelayed( + mBiometricMessageFollowUp != null + ? DEFAULT_HIDE_DELAY_MS * 2 + : DEFAULT_HIDE_DELAY_MS + ); updateBiometricMessage(); } private void hideBiometricMessage() { - if (mBiometricMessage != null) { + if (mBiometricMessage != null || mBiometricMessageFollowUp != null) { mBiometricMessage = null; + mBiometricMessageFollowUp = null; mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); updateBiometricMessage(); } @@ -789,9 +811,9 @@ public class KeyguardIndicationController { // colors can be hard to read in low brightness. mTopIndicationView.setTextColor(Color.WHITE); - CharSequence newIndication = null; + CharSequence newIndication; if (!TextUtils.isEmpty(mBiometricMessage)) { - newIndication = mBiometricMessage; + newIndication = mBiometricMessage; // note: doesn't show mBiometricMessageFollowUp } else if (!TextUtils.isEmpty(mTransientIndication)) { newIndication = mTransientIndication; } else if (!mBatteryPresent) { @@ -909,15 +931,21 @@ public class KeyguardIndicationController { || mAccessibilityManager.isTouchExplorationEnabled(); if (udfpsSupported && faceAuthenticated) { // co-ex if (a11yEnabled) { - showBiometricMessage(mContext.getString( - R.string.keyguard_face_successful_unlock_swipe)); + showBiometricMessage( + mContext.getString(R.string.keyguard_face_successful_unlock), + mContext.getString(R.string.keyguard_unlock) + ); } else { - showBiometricMessage(mContext.getString( - R.string.keyguard_face_successful_unlock_press)); + showBiometricMessage( + mContext.getString(R.string.keyguard_face_successful_unlock), + mContext.getString(R.string.keyguard_unlock_press) + ); } } else if (faceAuthenticated) { // face-only - showBiometricMessage(mContext.getString( - R.string.keyguard_face_successful_unlock_swipe)); + showBiometricMessage( + mContext.getString(R.string.keyguard_face_successful_unlock), + mContext.getString(R.string.keyguard_unlock) + ); } else if (udfpsSupported) { // udfps-only if (a11yEnabled) { showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); @@ -943,10 +971,11 @@ public class KeyguardIndicationController { pw.println(" mPowerCharged: " + mPowerCharged); pw.println(" mChargingSpeed: " + mChargingSpeed); pw.println(" mChargingWattage: " + mChargingWattage); - pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); + pw.println(" mMessageToShowOnScreenOn: " + mBiometricErrorMessageToShowOnScreenOn); pw.println(" mDozing: " + mDozing); pw.println(" mTransientIndication: " + mTransientIndication); pw.println(" mBiometricMessage: " + mBiometricMessage); + pw.println(" mBiometricMessageFollowUp: " + mBiometricMessageFollowUp); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); pw.println(" AOD text: " + ( @@ -958,8 +987,6 @@ public class KeyguardIndicationController { } protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { - public static final int HIDE_DELAY_MS = 5000; - @Override public void onTimeChanged() { if (mVisible) { @@ -1077,7 +1104,7 @@ public class KeyguardIndicationController { } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { showBiometricMessage(errString); } else { - mMessageToShowOnScreenOn = errString; + mBiometricErrorMessageToShowOnScreenOn = errString; } } @@ -1139,7 +1166,7 @@ public class KeyguardIndicationController { // Let's hide any previous messages when authentication starts, otherwise // multiple auth attempts would overlap. hideBiometricMessage(); - mMessageToShowOnScreenOn = null; + mBiometricErrorMessageToShowOnScreenOn = null; } } @@ -1179,7 +1206,7 @@ public class KeyguardIndicationController { @Override public void onRequireUnlockForNfc() { showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc)); - hideTransientIndicationDelayed(HIDE_DELAY_MS); + hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index 339f371c0d12..d24469e8421e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -39,6 +39,8 @@ import com.android.systemui.keyguard.KeyguardIndication; * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open"). */ public class KeyguardIndicationTextView extends TextView { + public static final long Y_IN_DURATION = 600L; + @StyleRes private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea; @StyleRes @@ -259,7 +261,7 @@ public class KeyguardIndicationTextView extends TextView { private long getYInDuration() { if (!mAnimationsEnabled) return 0L; - return 600L; + return Y_IN_DURATION; } private long getFadeOutDuration() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index c67737136b3b..cde30af447f6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; @@ -1005,7 +1006,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // WHEN udfps is now enrolled when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - callback.onEnrollmentsChanged(); + callback.onEnrollmentsChanged(TYPE_FINGERPRINT); // THEN isUdfspEnrolled is TRUE assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index d158892e4ec5..e0d1f7a19130 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -60,6 +60,9 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -154,7 +157,9 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mInteractionJankMonitor; @Captor - ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor; + ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor; + @Captor + ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor; @Captor ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor; @Captor @@ -193,25 +198,38 @@ public class AuthControllerTest extends SysuiTestCase { when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point()); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); - - final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); - componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, - "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, - "00000001" /* serialNumber */, "" /* softwareVersion */)); - componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */, - "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, - "vendor/version/revision" /* softwareVersion */)); - - FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal( - 1 /* sensorId */, - SensorProperties.STRENGTH_STRONG, - 1 /* maxEnrollmentsPerUser */, - componentInfo, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, - true /* resetLockoutRequireHardwareAuthToken */); - List<FingerprintSensorPropertiesInternal> props = new ArrayList<>(); - props.add(prop); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); + when(mFaceManager.isHardwareDetected()).thenReturn(true); + + final List<ComponentInfoInternal> fpComponentInfo = List.of( + new ComponentInfoInternal("faceSensor" /* componentId */, + "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */, + "00000001" /* serialNumber */, "" /* softwareVersion */)); + final List<ComponentInfoInternal> faceComponentInfo = List.of( + new ComponentInfoInternal("matchingAlgorithm" /* componentId */, + "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, + "vendor/version/revision" /* softwareVersion */)); + + final List<FingerprintSensorPropertiesInternal> fpProps = List.of( + new FingerprintSensorPropertiesInternal( + 1 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + fpComponentInfo, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + true /* resetLockoutRequireHardwareAuthToken */)); + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps); + + final List<FaceSensorPropertiesInternal> faceProps = List.of( + new FaceSensorPropertiesInternal( + 2 /* sensorId */, + SensorProperties.STRENGTH_STRONG, + 1 /* maxEnrollmentsPerUser */, + fpComponentInfo, + FaceSensorProperties.TYPE_RGB, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + true /* resetLockoutRequireHardwareAuthToken */)); + when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps); mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, @@ -219,12 +237,15 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); when(mStatusBarStateController.isDozing()).thenReturn(false); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps); // Ensures that the operations posted on the handler get executed. mTestableLooper.processAllMessages(); @@ -237,6 +258,7 @@ public class AuthControllerTest extends SysuiTestCase { throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); + reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -246,21 +268,27 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); mTestableLooper.processAllMessages(); verify(mFingerprintManager, never()).registerBiometricStateListener(any()); + verify(mFaceManager, never()).registerBiometricStateListener(any()); - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener(any()); + verify(mFaceManager).registerBiometricStateListener(any()); } @Test public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException { // This test is sensitive to prior FingerprintManager interactions. reset(mFingerprintManager); + reset(mFaceManager); // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, @@ -270,18 +298,25 @@ public class AuthControllerTest extends SysuiTestCase { authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( - mAuthenticatorsRegisteredCaptor.capture()); + mFpAuthenticatorsRegisteredCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorsRegisteredCaptor.capture()); // Emulates a device with no authenticators (empty list). - mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>()); + mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); + mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of()); mTestableLooper.processAllMessages(); verify(mFingerprintManager).registerBiometricStateListener( mBiometricStateCaptor.capture()); + verify(mFaceManager).registerBiometricStateListener( + mBiometricStateCaptor.capture()); // Enrollments changed for an unknown sensor. - mBiometricStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */, - 0xbeef /* sensorId */, true /* hasEnrollments */); + for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) { + listener.onEnrollmentsChanged(0 /* userId */, + 0xbeef /* sensorId */, true /* hasEnrollments */); + } mTestableLooper.processAllMessages(); // Nothing should crash. @@ -827,4 +862,3 @@ public class AuthControllerTest extends SysuiTestCase { } } } - diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 9ffc5a57cef6..b33f9a7f3933 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; @@ -412,7 +414,7 @@ public class DozeSensorsTest extends SysuiTestCase { // WHEN enrollment changes to TRUE when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); - mAuthControllerCallback.onEnrollmentsChanged(); + mAuthControllerCallback.onEnrollmentsChanged(TYPE_FINGERPRINT); // THEN mConfigured = TRUE assertTrue(triggerSensor.mConfigured); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 24d051508fde..5ec6bdf3c00b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.keyguard; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; @@ -219,7 +221,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { Pair<Float, PointF> udfps = setupUdfps(); // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(); + mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); mDelayableExecutor.runAllReady(); // THEN lock icon view location is updated with the same coordinates as auth controller vals diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt index 002f23a9ed6d..024d3bd8eb0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt @@ -23,8 +23,12 @@ import android.graphics.Insets import android.graphics.Rect import android.hardware.HardwareBuffer import android.net.Uri -import android.view.WindowManager -import android.view.WindowManager.ScreenshotSource +import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD +import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER +import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN +import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION +import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE + import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler import com.android.internal.util.ScreenshotHelper.ScreenshotRequest import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback @@ -43,13 +47,12 @@ class RequestProcessorTest { @Test fun testFullScreenshot() { - val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD) + val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD) val onSavedListener = mock<Consumer<Uri>>() val callback = mock<RequestCallback>() val processor = RequestProcessor(controller) - processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener, - request, callback) + processor.processRequest(request, onSavedListener, callback) verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(), eq(onSavedListener), eq(callback)) @@ -57,13 +60,12 @@ class RequestProcessorTest { @Test fun testSelectedRegionScreenshot() { - val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD) + val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD) val onSavedListener = mock<Consumer<Uri>>() val callback = mock<RequestCallback>() val processor = RequestProcessor(controller) - processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener, - request, callback) + processor.processRequest(request, onSavedListener, callback) verify(controller).takeScreenshotPartial(/* topComponent */ isNull(), eq(onSavedListener), eq(callback)) @@ -82,14 +84,13 @@ class RequestProcessorTest { val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!! val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap) - val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle, - bounds, Insets.NONE, taskId, userId, topComponent) + val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER, + bitmapBundle, bounds, Insets.NONE, taskId, userId, topComponent) val onSavedListener = mock<Consumer<Uri>>() val callback = mock<RequestCallback>() - processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener, - request, callback) + processor.processRequest(request, onSavedListener, callback) verify(controller).handleImageAsScreenshot( bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index c5beee892172..22d61a71b93e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -23,6 +23,7 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; +import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO; @@ -50,7 +51,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.IActivityManager; import android.app.Instrumentation; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyResourcesManager; @@ -161,8 +161,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; @Mock - private IActivityManager mIActivityManager; - @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private AccessibilityManager mAccessibilityManager; @@ -256,8 +254,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils, - mScreenLifecycle, mIActivityManager, mKeyguardBypassController, - mAccessibilityManager); + mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager); mController.init(); mController.setIndicationArea(mIndicationArea); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); @@ -974,11 +971,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); - // THEN 'face unlocked. press unlock icon to open' message shows - String pressToOpen = mContext.getString(R.string.keyguard_face_successful_unlock_press); - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen); - - assertThat(mTextView.getText()).isNotEqualTo(pressToOpen); + // THEN 'face unlocked' then 'press unlock icon to open' message show + String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace); + String pressToOpen = mContext.getString(R.string.keyguard_unlock_press); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, pressToOpen); } @@ -999,10 +996,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); - // THEN show 'face unlocked. swipe up to open' message - String faceUnlockedSwipeToOpen = - mContext.getString(R.string.keyguard_face_successful_unlock_swipe); - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); + // THEN show 'face unlocked' and 'swipe up to open' messages + String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace); + String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen); } @Test @@ -1021,10 +1019,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); - // THEN show 'swipe up to open' message - String faceUnlockedSwipeToOpen = - mContext.getString(R.string.keyguard_face_successful_unlock_swipe); - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); + // THEN show 'face unlocked' and 'swipe up to open' messages + String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace); + String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen); } @Test @@ -1042,10 +1041,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); - // THEN show 'swipe up to open' message - String faceUnlockedSwipeToOpen = - mContext.getString(R.string.keyguard_face_successful_unlock_swipe); - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); + // THEN show 'face unlocked' and 'swipe up to open' messages + String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace); + String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen); } @Test diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml index 0eaf3592a510..9bf86f51f8fe 100644 --- a/packages/VpnDialogs/res/values-es/strings.xml +++ b/packages/VpnDialogs/res/values-es/strings.xml @@ -19,7 +19,7 @@ <string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string> <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string> <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparecerá en la pantalla cuando la VPN esté activa."</string> - <string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string> + <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada"</string> <string name="session" msgid="6470628549473641030">"Sesión:"</string> <string name="duration" msgid="3584782459928719435">"Duración:"</string> <string name="data_transmitted" msgid="7988167672982199061">"Enviado:"</string> diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index f903c2005ae3..a94bfe281be1 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -302,6 +302,10 @@ message SystemMessage { // Package: android NOTE_WIFI_APM_NOTIFICATION = 73; + // Inform the user of bluetooth apm state changes. + // Package: android + NOTE_BT_APM_NOTIFICATION = 74; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 1d457aa933d7..02c6ca20d34e 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -479,6 +479,10 @@ public final class DropBoxManagerService extends SystemService { if (length > max) { // Log and fall through to create empty tombstone below Slog.w(TAG, "Dropping: " + tag + " (" + length + " > " + max + " bytes)"); + logDropboxDropped( + FrameworkStatsLog.DROPBOX_ENTRY_DROPPED__DROP_REASON__ENTRY_TOO_LARGE, + tag, + 0); } else { temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp"); try (FileOutputStream out = new FileOutputStream(temp)) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 83e3b499fc96..4a3f682a592d 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3047,6 +3047,10 @@ class StorageManagerService extends IStorageManager.Stub try { mVold.createUserKey(userId, serialNumber, ephemeral); + // New keys are always unlocked. + synchronized (mLock) { + mLocalUnlockedUsers.append(userId); + } } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3058,6 +3062,10 @@ class StorageManagerService extends IStorageManager.Stub try { mVold.destroyUserKey(userId); + // Destroying a key also locks it. + synchronized (mLock) { + mLocalUnlockedUsers.remove(userId); + } } catch (Exception e) { Slog.wtf(TAG, e); } diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java index 91588913c33c..4380b42ee54c 100644 --- a/services/core/java/com/android/server/am/LmkdStatsReporter.java +++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java @@ -50,7 +50,8 @@ public final class LmkdStatsReporter { * Logs the event when LMKD kills a process to reduce memory pressure. * Code: LMK_KILL_OCCURRED = 51 */ - public static void logKillOccurred(DataInputStream inputData) { + public static void logKillOccurred(DataInputStream inputData, int totalForegroundServices, + int procsWithForegroundServices) { try { final long pgFault = inputData.readLong(); final long pgMajFault = inputData.readLong(); @@ -67,11 +68,10 @@ public final class LmkdStatsReporter { final int thrashing = inputData.readInt(); final int maxThrashing = inputData.readInt(); final String procName = inputData.readUTF(); - FrameworkStatsLog.write(FrameworkStatsLog.LMK_KILL_OCCURRED, uid, procName, oomScore, pgFault, pgMajFault, rssInBytes, cacheInBytes, swapInBytes, processStartTimeNS, minOomScore, freeMemKb, freeSwapKb, mapKillReason(killReason), thrashing, - maxThrashing); + maxThrashing, totalForegroundServices, procsWithForegroundServices); } catch (IOException e) { Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED"); return; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 42792bf64f44..ccbca76d1868 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -814,7 +814,12 @@ public final class ProcessList { < LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) { return false; } - LmkdStatsReporter.logKillOccurred(inputData); + Pair<Integer, Integer> temp = getNumForegroundServices(); + final int totalForegroundServices = temp.first; + final int procsWithForegroundServices = temp.second; + LmkdStatsReporter.logKillOccurred(inputData, + totalForegroundServices, + procsWithForegroundServices); return true; case LMK_STATE_CHANGED: if (receivedLen @@ -5123,6 +5128,26 @@ public final class ProcessList { } } + /** + * Get the number of foreground services in all processes and number of processes that have + * foreground service within. + */ + Pair<Integer, Integer> getNumForegroundServices() { + int numForegroundServices = 0; + int procs = 0; + synchronized (mService) { + for (int i = 0, size = mLruProcesses.size(); i < size; i++) { + ProcessRecord pr = mLruProcesses.get(i); + int numFgs = pr.mServices.getNumForegroundServices(); + if (numFgs > 0) { + numForegroundServices += numFgs; + procs++; + } + } + } + return new Pair<>(numForegroundServices, procs); + } + private final class ImperceptibleKillRunner extends IUidObserver.Stub { private static final String EXTRA_PID = "pid"; private static final String EXTRA_UID = "uid"; diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index 9951e983a752..67eb675503ad 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -180,6 +180,16 @@ final class ProcessServiceRecord { mRepFgServiceTypes = foregroundServiceTypes; } + int getNumForegroundServices() { + int count = 0; + for (int i = 0, serviceCount = mServices.size(); i < serviceCount; i++) { + if (mServices.valueAt(i).isForeground) { + count++; + } + } + return count; + } + void updateHasTopStartedAlmostPerceptibleServices() { mHasTopStartedAlmostPerceptibleServices = false; mLastTopStartedAlmostPerceptibleBindRequestUptimeMs = 0; diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index d0817b078b61..470de8ce0955 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -99,6 +99,7 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_SWCODEC_NATIVE, DeviceConfig.NAMESPACE_TETHERING, DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE, + DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE_BOOT, DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE, DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_MEMORY_SAFETY_NATIVE, diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index d16fe1240d0c..d4ef638d0818 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -343,6 +343,9 @@ public class AttentionManagerService extends SystemService { * * Calling this multiple times for duplicate requests will be no-ops, returning true. * + * TODO(b/239130847): Maintain the proximity state in AttentionManagerService and change this + * to a polling API. + * * @return {@code true} if the framework was able to dispatch the request */ @VisibleForTesting @@ -853,9 +856,6 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") private void cancelAndUnbindLocked() { synchronized (mLock) { - if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) { - return; - } if (mCurrentAttentionCheck != null) { cancel(); } @@ -937,7 +937,7 @@ public class AttentionManagerService extends SystemService { } } - class TestableProximityUpdateCallbackInternal extends ProximityUpdateCallbackInternal { + class TestableProximityUpdateCallbackInternal implements ProximityUpdateCallbackInternal { private double mLastCallbackCode = PROXIMITY_UNKNOWN; @Override @@ -1069,6 +1069,7 @@ public class AttentionManagerService extends SystemService { private void resetStates() { synchronized (mLock) { mCurrentProximityUpdate = null; + cancelAndUnbindLocked(); } mComponentName = resolveAttentionService(mContext); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java new file mode 100644 index 000000000000..0f1fe68ad1d7 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.annotation.NonNull; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.util.proto.ProtoOutputStream; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + +/** + * Common attributes for all biometric service providers. + * + * @param <T> Internal settings type. + */ +public interface BiometricServiceProvider<T extends SensorPropertiesInternal> { + + /** Checks if the specified sensor is owned by this provider. */ + boolean containsSensor(int sensorId); + + /** All sensor properties. */ + @NonNull + List<T> getSensorProperties(); + + /** Properties for the given sensor id. */ + @NonNull + T getSensorProperties(int sensorId); + + boolean isHardwareDetected(int sensorId); + + /** If the user has any enrollments for the given sensor. */ + boolean hasEnrollments(int sensorId, int userId); + + long getAuthenticatorId(int sensorId, int userId); + + @LockoutTracker.LockoutMode + int getLockoutModeForUser(int sensorId, int userId); + + void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, + boolean clearSchedulerBuffer); + + void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); + + void dumpInternal(int sensorId, @NonNull PrintWriter pw); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java new file mode 100644 index 000000000000..7574523f0662 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.os.Handler; +import android.os.IInterface; +import android.os.Process; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Pair; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +/** + * Container for all BiometricServiceProvider implementations. + * + * @param <T> The service provider type. + * @param <P> The internal properties type. + * @param <C> The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}. + */ +public abstract class BiometricServiceRegistry<T extends BiometricServiceProvider<P>, + P extends SensorPropertiesInternal, + C extends IInterface> { + + private static final String TAG = "BiometricServiceRegistry"; + + // Volatile so they can be read without a lock once all services are registered. + // But, ideally remove this and provide immutable copies via the callback instead. + @Nullable + private volatile List<T> mServiceProviders; + @Nullable + private volatile List<P> mAllProps; + + @NonNull + private final Supplier<IBiometricService> mBiometricServiceSupplier; + @NonNull + private final RemoteCallbackList<C> mRegisteredCallbacks = new RemoteCallbackList<>(); + + public BiometricServiceRegistry(@NonNull Supplier<IBiometricService> biometricSupplier) { + mBiometricServiceSupplier = biometricSupplier; + } + + /** + * Register an implementation by creating a new authenticator and initializing it via + * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)} + * using the given properties. + * + * @param service service to register with + * @param props internal properties to initialize the authenticator + */ + protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props); + + /** + * Invoke the callback to notify clients that all authenticators have been registered. + * + * @param callback callback to invoke + * @param allProps properties of all authenticators + */ + protected abstract void invokeRegisteredCallback(@NonNull C callback, + @NonNull List<P> allProps) throws RemoteException; + + /** + * Register all authenticators in a background thread. + * + * @param serviceProvider Supplier function that will be invoked on the background thread. + */ + public void registerAll(Supplier<List<T>> serviceProvider) { + // Some HAL might not be started before the system service and will cause the code below + // to wait, and some of the operations below might take a significant amount of time to + // complete (calls to the HALs). To avoid blocking the rest of system server we put + // this on a background thread. + final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, + true /* allowIo */); + thread.start(); + final Handler handler = new Handler(thread.getLooper()); + handler.post(() -> registerAllInBackground(serviceProvider)); + thread.quitSafely(); + } + + /** Register authenticators now, only called by {@link #registerAll(Supplier).} */ + @VisibleForTesting + public void registerAllInBackground(Supplier<List<T>> serviceProvider) { + List<T> providers = serviceProvider.get(); + if (providers == null) { + providers = new ArrayList<>(); + } + + final IBiometricService biometricService = mBiometricServiceSupplier.get(); + if (biometricService == null) { + throw new IllegalStateException("biometric service cannot be null"); + } + + // Register each sensor individually with BiometricService + final List<P> allProps = new ArrayList<>(); + for (T provider : providers) { + final List<P> props = provider.getSensorProperties(); + for (P prop : props) { + registerService(biometricService, prop); + } + allProps.addAll(props); + } + + finishRegistration(providers, allProps); + } + + private synchronized void finishRegistration( + @NonNull List<T> providers, @NonNull List<P> allProps) { + mServiceProviders = Collections.unmodifiableList(providers); + mAllProps = Collections.unmodifiableList(allProps); + broadcastAllAuthenticatorsRegistered(); + } + + /** + * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)} + * has finished registering all providers (executes immediately if already done). + * + * @param callback registration callback + */ + public synchronized void addAllRegisteredCallback(@Nullable C callback) { + if (callback == null) { + Slog.e(TAG, "addAllRegisteredCallback, callback is null"); + return; + } + + final boolean registered = mRegisteredCallbacks.register(callback); + final boolean allRegistered = mServiceProviders != null; + if (registered && allRegistered) { + broadcastAllAuthenticatorsRegistered(); + } else if (!registered) { + Slog.e(TAG, "addAllRegisteredCallback failed to register callback"); + } + } + + private synchronized void broadcastAllAuthenticatorsRegistered() { + final int n = mRegisteredCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + final C cb = mRegisteredCallbacks.getBroadcastItem(i); + try { + invokeRegisteredCallback(cb, mAllProps); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e); + } finally { + mRegisteredCallbacks.unregister(cb); + } + } + mRegisteredCallbacks.finishBroadcast(); + } + + /** + * Get a list of registered providers. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @NonNull + public List<T> getProviders() { + return mServiceProviders != null ? mServiceProviders : Collections.emptyList(); + } + + /** + * Gets the provider for given sensor id or null if not registered. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @Nullable + public T getProviderForSensor(int sensorId) { + if (mServiceProviders != null) { + for (T provider : mServiceProviders) { + if (provider.containsSensor(sensorId)) { + return provider; + } + } + } + return null; + } + + /** + * For devices with only a single provider, returns that provider. + * If no providers, or multiple providers exist, returns null. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @Nullable + public Pair<Integer, T> getSingleProvider() { + if (mAllProps == null || mAllProps.size() != 1) { + Slog.e(TAG, "Multiple sensors found: " + mAllProps.size()); + return null; + } + + final int sensorId = mAllProps.get(0).sensorId; + final T provider = getProviderForSensor(sensorId); + if (provider != null) { + return new Pair<>(sensorId, provider); + } + + Slog.e(TAG, "Single sensor, but provider not found"); + return null; + } + + /** + * Get the properties for all providers. + * + * Undefined until {@link #registerAll(Supplier)} has fired the completion callback. + */ + @NonNull + public List<P> getAllProperties() { + return mAllProps != null ? mAllProps : Collections.emptyList(); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java index 0d789f7a1840..f8543162f95e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java @@ -23,32 +23,64 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE; import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.IBiometricStateListener; +import android.hardware.biometrics.SensorPropertiesInternal; import android.os.RemoteException; +import android.os.UserManager; import android.util.Slog; import com.android.server.biometrics.Utils; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * A callback for receiving notifications about biometric sensor state changes. + * + * @param <T> service provider type + * @param <P> internal property type */ -public class BiometricStateCallback implements ClientMonitorCallback { +public class BiometricStateCallback<T extends BiometricServiceProvider<P>, + P extends SensorPropertiesInternal> implements ClientMonitorCallback { private static final String TAG = "BiometricStateCallback"; @NonNull - private final CopyOnWriteArrayList<IBiometricStateListener> - mBiometricStateListeners = new CopyOnWriteArrayList<>(); - - private @BiometricStateListener.State int mBiometricState; + private final CopyOnWriteArrayList<IBiometricStateListener> mBiometricStateListeners = + new CopyOnWriteArrayList<>(); + @NonNull + private final UserManager mUserManager; + @BiometricStateListener.State + private int mBiometricState; + @NonNull + private List<T> mProviders = List.of(); - public BiometricStateCallback() { + /** + * Create a new callback that must be {@link #start(List)}ed. + * + * @param userManager user manager + */ + public BiometricStateCallback(@NonNull UserManager userManager) { mBiometricState = STATE_IDLE; + mUserManager = userManager; + } + + /** + * This should be called when the service has been initialized and all providers are ready. + * + * @param allProviders all registered biometric service providers + */ + public synchronized void start(@NonNull List<T> allProviders) { + mProviders = Collections.unmodifiableList(allProviders); + broadcastCurrentEnrollmentState(null /* listener */); } + /** Get the current state. */ + @BiometricStateListener.State public int getBiometricState() { return mBiometricState; } @@ -120,23 +152,43 @@ public class BiometricStateCallback implements ClientMonitorCallback { } /** - * This should be invoked when: - * 1) Enrolled --> None-enrolled - * 2) None-enrolled --> enrolled - * 3) HAL becomes ready - * 4) Listener is registered + * Enables clients to register a BiometricStateListener. For example, this is used to forward + * fingerprint sensor state changes to SideFpsEventHandler. + * + * @param listener listener to register */ - public void notifyAllEnrollmentStateChanged(int userId, int sensorId, + public synchronized void registerBiometricStateListener( + @NonNull IBiometricStateListener listener) { + mBiometricStateListeners.add(listener); + broadcastCurrentEnrollmentState(listener); + } + + private synchronized void broadcastCurrentEnrollmentState( + @Nullable IBiometricStateListener listener) { + for (T provider : mProviders) { + for (SensorPropertiesInternal prop : provider.getSensorProperties()) { + for (UserInfo userInfo : mUserManager.getAliveUsers()) { + final boolean enrolled = provider.hasEnrollments(prop.sensorId, userInfo.id); + if (listener != null) { + notifyEnrollmentStateChanged( + listener, userInfo.id, prop.sensorId, enrolled); + } else { + notifyAllEnrollmentStateChanged( + userInfo.id, prop.sensorId, enrolled); + } + } + } + } + } + + private void notifyAllEnrollmentStateChanged(int userId, int sensorId, boolean hasEnrollments) { for (IBiometricStateListener listener : mBiometricStateListeners) { notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments); } } - /** - * Notifies the listener of enrollment state changes. - */ - public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, + private void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener, int userId, int sensorId, boolean hasEnrollments) { try { listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments); @@ -144,14 +196,4 @@ public class BiometricStateCallback implements ClientMonitorCallback { Slog.e(TAG, "Remote exception", e); } } - - /** - * Enables clients to register a BiometricStateListener. For example, this is used to forward - * fingerprint sensor state changes to SideFpsEventHandler. - * - * @param listener - */ - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateListeners.add(listener); - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 79e65cc6d2e5..271bce9890c6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -17,20 +17,18 @@ package com.android.server.biometrics.sensors.face; import static android.Manifest.permission.INTERACT_ACROSS_USERS; -import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FACE; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; -import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricStateListener; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; @@ -39,18 +37,18 @@ import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceServiceReceiver; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.face.IFaceService; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.NativeHandle; -import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.UserHandle; +import android.os.UserManager; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -58,10 +56,10 @@ import android.view.Surface; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; @@ -88,51 +86,10 @@ public class FaceService extends SystemService { private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockPatternUtils mLockPatternUtils; @NonNull - private final List<ServiceProvider> mServiceProviders; - - @Nullable - private ServiceProvider getProviderForSensor(int sensorId) { - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return provider; - } - } - return null; - } - - /** - * For devices with only a single provider, returns that provider. If no providers, or multiple - * providers exist, returns null. - */ - @Nullable - private Pair<Integer, ServiceProvider> getSingleProvider() { - final List<FaceSensorPropertiesInternal> properties = getSensorProperties(); - if (properties.size() != 1) { - Slog.e(TAG, "Multiple sensors found: " + properties.size()); - return null; - } - - // Theoretically we can just return the first provider, but maybe this is easier to - // understand. - final int sensorId = properties.get(0).sensorId; - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return new Pair<>(sensorId, provider); - } - } - - Slog.e(TAG, "Single sensor, but provider not found"); - return null; - } - + private final FaceServiceRegistry mRegistry; @NonNull - private List<FaceSensorPropertiesInternal> getSensorProperties() { - final List<FaceSensorPropertiesInternal> properties = new ArrayList<>(); - for (ServiceProvider provider : mServiceProviders) { - properties.addAll(provider.getSensorProperties()); - } - return properties; - } + private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal> + mBiometricStateCallback; /** * Receives the incoming binder calls from FaceManager. @@ -142,8 +99,7 @@ public class FaceService extends SystemService { @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -156,9 +112,8 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { - final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -170,16 +125,14 @@ public class FaceService extends SystemService { @Override // Binder call public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal( String opPackageName) { - - return FaceService.this.getSensorProperties(); + return mRegistry.getAllProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public FaceSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -193,8 +146,7 @@ public class FaceService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFaceServiceReceiver receiver, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -207,8 +159,7 @@ public class FaceService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -222,8 +173,7 @@ public class FaceService extends SystemService { public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -245,8 +195,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -260,7 +209,6 @@ public class FaceService extends SystemService { public long authenticate(final IBinder token, final long operationId, int userId, final IFaceServiceReceiver receiver, final String opPackageName, boolean isKeyguardBypassEnabled) { - // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or // lockdown, something wrong happened. See similar path in FingerprintService. @@ -273,7 +221,7 @@ public class FaceService extends SystemService { // permission. final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); return -1; @@ -301,7 +249,7 @@ public class FaceService extends SystemService { return -1; } - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFace"); return -1; @@ -318,8 +266,7 @@ public class FaceService extends SystemService { IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -336,8 +283,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -350,8 +296,7 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthentication(final IBinder token, final String opPackageName, final long requestId) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -370,7 +315,7 @@ public class FaceService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFaceDetect"); return; @@ -383,8 +328,7 @@ public class FaceService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(int sensorId, final IBinder token, final String opPackageName, final long requestId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -397,8 +341,7 @@ public class FaceService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int faceId, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -412,7 +355,6 @@ public class FaceService extends SystemService { @Override // Binder call public void removeAll(final IBinder token, final int userId, final IFaceServiceReceiver receiver, final String opPackageName) { - final FaceServiceReceiver internalReceiver = new FaceServiceReceiver() { int sensorsFinishedRemoving = 0; final int numSensors = getSensorPropertiesInternal( @@ -432,7 +374,7 @@ public class FaceService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { List<FaceSensorPropertiesInternal> props = provider.getSensorProperties(); for (FaceSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, userId, internalReceiver, @@ -467,27 +409,27 @@ public class FaceService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); } } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else if (args.length > 1 && "--hal".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpHal(props.sensorId, fd, Arrays.copyOfRange(args, 1, args.length, args.getClass())); } } } else { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId + ", provider: " + provider.getClass().getSimpleName()); @@ -504,10 +446,9 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { - final long token = Binder.clearCallingIdentity(); try { - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -521,12 +462,11 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public List<Face> getEnrolledFaces(int sensorId, int userId, String opPackageName) { - if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName); return Collections.emptyList(); @@ -538,12 +478,11 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) { - if (userId != UserHandle.getCallingUserId()) { Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS); } - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName); return false; @@ -555,8 +494,7 @@ public class FaceService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -569,8 +507,7 @@ public class FaceService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -582,7 +519,7 @@ public class FaceService extends SystemService { @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -595,8 +532,7 @@ public class FaceService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -610,8 +546,7 @@ public class FaceService extends SystemService { public void setFeature(final IBinder token, int userId, int feature, boolean enabled, final byte[] hardwareAuthToken, IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for setFeature"); return; @@ -625,8 +560,7 @@ public class FaceService extends SystemService { @Override public void getFeature(final IBinder token, int userId, int feature, IFaceServiceReceiver receiver, final String opPackageName) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getFeature"); return; @@ -636,18 +570,14 @@ public class FaceService extends SystemService { new ClientMonitorCallbackConverter(receiver), opPackageName); } - private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) { - for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { - mServiceProviders.add( - Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); - } - } + private List<ServiceProvider> getAidlProviders() { + final List<ServiceProvider> providers = new ArrayList<>(); - private void addAidlProviders() { final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR); if (instances == null || instances.length == 0) { - return; + return providers; } + for (String instance : instances) { final String fqName = IFace.DESCRIPTOR + "/" + instance; final IFace face = IFace.Stub.asInterface( @@ -660,53 +590,41 @@ public class FaceService extends SystemService { final SensorProps[] props = face.getSensorProps(); final FaceProvider provider = new FaceProvider(getContext(), props, instance, mLockoutResetDispatcher, BiometricContext.getInstance(getContext())); - mServiceProviders.add(provider); + providers.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } } + + return providers; } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public void registerAuthenticators( @NonNull List<FaceSensorPropertiesInternal> hidlSensors) { - - // Some HAL might not be started before the system service and will cause the code below - // to wait, and some of the operations below might take a significant amount of time to - // complete (calls to the HALs). To avoid blocking the rest of system server we put - // this on a background thread. - final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, - true /* allowIo */); - thread.start(); - final Handler handler = new Handler(thread.getLooper()); - - handler.post(() -> { - addHidlProviders(hidlSensors); - addAidlProviders(); - - final IBiometricService biometricService = IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE)); - - // Register each sensor individually with BiometricService - for (ServiceProvider provider : mServiceProviders) { - final List<FaceSensorPropertiesInternal> props = provider.getSensorProperties(); - for (FaceSensorPropertiesInternal prop : props) { - final int sensorId = prop.sensorId; - final @BiometricManager.Authenticators.Types int strength = - Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); - final FaceAuthenticator authenticator = new FaceAuthenticator( - mServiceWrapper, sensorId); - try { - biometricService.registerAuthenticator(sensorId, TYPE_FACE, strength, - authenticator); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); - } - } + mRegistry.registerAll(() -> { + final List<ServiceProvider> providers = new ArrayList<>(); + for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { + providers.add( + Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); } + providers.addAll(getAidlProviders()); + return providers; }); } + + @Override + public void addAuthenticatorsRegisteredCallback( + IFaceAuthenticatorsRegisteredCallback callback) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + mRegistry.addAllRegisteredCallback(callback); + } + + @Override + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateCallback.registerBiometricStateListener(listener); + } } public FaceService(Context context) { @@ -714,7 +632,16 @@ public class FaceService extends SystemService { mServiceWrapper = new FaceServiceWrapper(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mServiceProviders = new ArrayList<>(); + mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); + mRegistry = new FaceServiceRegistry(mServiceWrapper, + () -> IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE))); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) { + mBiometricStateCallback.start(mRegistry.getProviders()); + } + }); } @Override @@ -752,7 +679,7 @@ public class FaceService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java new file mode 100644 index 000000000000..0f0a81d24473 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.IBiometricService; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; +import android.hardware.face.IFaceService; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceRegistry; + +import java.util.List; +import java.util.function.Supplier; + +/** Registry for {@link IFaceService} providers. */ +public class FaceServiceRegistry extends BiometricServiceRegistry<ServiceProvider, + FaceSensorPropertiesInternal, IFaceAuthenticatorsRegisteredCallback> { + + private static final String TAG = "FaceServiceRegistry"; + + @NonNull + private final IFaceService mService; + + /** Creates a new registry tied to the given service. */ + public FaceServiceRegistry(@NonNull IFaceService service, + @Nullable Supplier<IBiometricService> biometricSupplier) { + super(biometricSupplier); + mService = service; + } + + @Override + protected void registerService(@NonNull IBiometricService service, + @NonNull FaceSensorPropertiesInternal props) { + @BiometricManager.Authenticators.Types final int strength = + Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); + try { + service.registerAuthenticator(props.sensorId, TYPE_FACE, strength, + new FaceAuthenticator(mService, props.sensorId)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); + } + } + + @Override + protected void invokeRegisteredCallback(@NonNull IFaceAuthenticatorsRegisteredCallback callback, + @NonNull List<FaceSensorPropertiesInternal> allProps) throws RemoteException { + callback.onAllAuthenticatorsRegistered(allProps); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 6f98365332e2..4efaedbd5530 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -26,15 +26,13 @@ import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.IBinder; -import android.util.proto.ProtoOutputStream; import android.view.Surface; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; -import com.android.server.biometrics.sensors.LockoutTracker; import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.List; /** @@ -56,24 +54,11 @@ import java.util.List; * to check (e.g. via {@link FaceManager#getSensorPropertiesInternal()}) that the code path isn't * taken. ServiceProviders will provide a no-op for unsupported operations to fail safely. */ -public interface ServiceProvider { - /** - * Checks if the specified sensor is owned by this provider. - */ - boolean containsSensor(int sensorId); - - @NonNull - List<FaceSensorPropertiesInternal> getSensorProperties(); - - @NonNull - FaceSensorPropertiesInternal getSensorProperties(int sensorId); +public interface ServiceProvider extends BiometricServiceProvider<FaceSensorPropertiesInternal> { @NonNull List<Face> getEnrolledFaces(int sensorId, int userId); - @LockoutTracker.LockoutMode - int getLockoutModeForUser(int sensorId, int userId); - /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be * invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -84,10 +69,6 @@ public interface ServiceProvider { + " this method"); } - long getAuthenticatorId(int sensorId, int userId); - - boolean isHardwareDetected(int sensorId); - void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, String opPackageName); @@ -142,13 +123,6 @@ public interface ServiceProvider { void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); - void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, - boolean clearSchedulerBuffer); - - void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); - - void dumpInternal(int sensorId, @NonNull PrintWriter pw); - @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 19d54c84c706..6bff179e8eb7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -285,6 +285,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFaces(sensorId, userId).isEmpty(); + } + + @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 65289122747c..c0a119ff5f1e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -484,6 +484,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFaces(sensorId, userId).isEmpty(); + } + + @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { return mLockoutTracker.getLockoutModeForUser(userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 2ba449ae089e..7e2742edd47a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -17,14 +17,11 @@ package com.android.server.biometrics.sensors.fingerprint; import static android.Manifest.permission.INTERACT_ACROSS_USERS; -import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.MANAGE_FINGERPRINT; -import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; import static android.Manifest.permission.TEST_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; @@ -36,8 +33,6 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -65,7 +60,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Process; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -79,11 +73,9 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; @@ -115,74 +107,32 @@ public class FingerprintService extends SystemService { protected static final String TAG = "FingerprintService"; - private final Object mLock = new Object(); private final AppOpsManager mAppOps; private final LockoutResetDispatcher mLockoutResetDispatcher; private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; private final LockPatternUtils mLockPatternUtils; - @NonNull private final List<ServiceProvider> mServiceProviders; - @NonNull private final BiometricStateCallback mBiometricStateCallback; - @NonNull private final Handler mHandler; - @NonNull private final BiometricContext mBiometricContext; - @NonNull private final Supplier<IBiometricService> mBiometricServiceSupplier; - @NonNull private final Function<String, IFingerprint> mIFingerprintProvider; - - @GuardedBy("mLock") - @NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback> - mAuthenticatorsRegisteredCallbacks; - - @GuardedBy("mLock") - @NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps; - - /** - * Registers BiometricStateListener in list stored by FingerprintService - * @param listener new BiometricStateListener being added - */ - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - mBiometricStateCallback.registerBiometricStateListener(listener); - broadcastCurrentEnrollmentState(listener); - } - - /** - * @param listener if non-null, notifies only this listener. if null, notifies all listeners - * in {@link BiometricStateCallback}. This is slightly ugly, but reduces - * redundant code. - */ - private void broadcastCurrentEnrollmentState(@Nullable IBiometricStateListener listener) { - final UserManager um = UserManager.get(getContext()); - synchronized (mLock) { - // Update the new listener with current state of all sensors - for (FingerprintSensorPropertiesInternal prop : mSensorProps) { - final ServiceProvider provider = getProviderForSensor(prop.sensorId); - for (UserInfo userInfo : um.getAliveUsers()) { - final boolean enrolled = !provider - .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty(); - - // Defer this work and allow the loop to release the lock sooner - mHandler.post(() -> { - if (listener != null) { - mBiometricStateCallback.notifyEnrollmentStateChanged( - listener, userInfo.id, prop.sensorId, enrolled); - } else { - mBiometricStateCallback.notifyAllEnrollmentStateChanged( - userInfo.id, prop.sensorId, enrolled); - } - }); - } - } - } - } + @NonNull + private final BiometricContext mBiometricContext; + @NonNull + private final Supplier<String[]> mAidlInstanceNameSupplier; + @NonNull + private final Function<String, IFingerprint> mIFingerprintProvider; + @NonNull + private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal> + mBiometricStateCallback; + @NonNull + private final Handler mHandler; + @NonNull + private final FingerprintServiceRegistry mRegistry; - /** - * Receives the incoming binder calls from FingerprintManager. - */ - private final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { + /** Receives the incoming binder calls from FingerprintManager. */ + @VisibleForTesting + final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC) @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId); @@ -195,9 +145,8 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) { - final ProtoOutputStream proto = new ProtoOutputStream(); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider != null) { provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer); } @@ -212,16 +161,14 @@ public class FingerprintService extends SystemService { != PackageManager.PERMISSION_GRANTED) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } - - return FingerprintService.this.getSensorProperties(); + return mRegistry.getAllProperties(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId, @NonNull String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId + ", caller: " + opPackageName); @@ -234,8 +181,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId); return; @@ -248,8 +194,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; @@ -264,8 +209,7 @@ public class FingerprintService extends SystemService { public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); return -1; @@ -278,8 +222,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT) @Override // Binder call public void cancelEnrollment(final IBinder token, long requestId) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelEnrollment"); return; @@ -339,10 +282,10 @@ public class FingerprintService extends SystemService { final Pair<Integer, ServiceProvider> provider; if (sensorId == FingerprintManager.SENSOR_ID_ANY) { - provider = getSingleProvider(); + provider = mRegistry.getSingleProvider(); } else { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - provider = new Pair<>(sensorId, getProviderForSensor(sensorId)); + provider = new Pair<>(sensorId, mRegistry.getProviderForSensor(sensorId)); } if (provider == null) { Slog.w(TAG, "Null provider for authenticate"); @@ -374,7 +317,6 @@ public class FingerprintService extends SystemService { final IFingerprintServiceReceiver receiver, final String opPackageName, boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException { - final Context context = getUiContext(); final Context promptContext = context.createPackageContextAsUser( opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(uId)); @@ -468,7 +410,7 @@ public class FingerprintService extends SystemService { return -1; } - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); return -1; @@ -484,8 +426,7 @@ public class FingerprintService extends SystemService { public void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for prepareForAuthentication"); return; @@ -501,8 +442,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC) @Override // Binder call public void startPreparedClient(int sensorId, int cookie) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for startPreparedClient"); return; @@ -532,7 +472,7 @@ public class FingerprintService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthentication"); return; @@ -553,7 +493,7 @@ public class FingerprintService extends SystemService { // For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as // cancelling authentication. - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for cancelFingerprintDetect"); return; @@ -566,11 +506,9 @@ public class FingerprintService extends SystemService { @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName, final long requestId) { - - Slog.d(TAG, "cancelAuthenticationFromService, sensorId: " + sensorId); - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for cancelAuthenticationFromService"); return; @@ -583,8 +521,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void remove(final IBinder token, final int fingerId, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName) { - - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for remove"); return; @@ -617,7 +554,7 @@ public class FingerprintService extends SystemService { // This effectively iterates through all sensors, but has to do so by finding all // sensors under each provider. - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties(); for (FingerprintSensorPropertiesInternal prop : props) { provider.scheduleRemoveAll(prop.sensorId, token, internalReceiver, userId, @@ -652,7 +589,7 @@ public class FingerprintService extends SystemService { try { if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoState(props.sensorId, proto, false); @@ -660,14 +597,14 @@ public class FingerprintService extends SystemService { } proto.flush(); } else if (args.length > 0 && "--proto".equals(args[0])) { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.dumpProtoMetrics(props.sensorId, fd); } } } else { - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { pw.println("Dumping for sensorId: " + props.sensorId @@ -698,7 +635,7 @@ public class FingerprintService extends SystemService { final long token = Binder.clearCallingIdentity(); try { - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: " + opPackageName); @@ -713,8 +650,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public boolean isHardwareDetected(int sensorId, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName); return false; @@ -730,7 +666,7 @@ public class FingerprintService extends SystemService { return; } - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for rename"); return; @@ -781,8 +717,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for hasEnrolledFingerprints, caller: " + opPackageName); return false; @@ -794,8 +729,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getLockoutModeForUser"); return LockoutTracker.LOCKOUT_NONE; @@ -807,8 +741,7 @@ public class FingerprintService extends SystemService { @Override public void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); return; @@ -819,8 +752,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for getAuthenticatorId"); return 0; @@ -832,8 +764,7 @@ public class FingerprintService extends SystemService { @Override // Binder call public void resetLockout(IBinder token, int sensorId, int userId, @Nullable byte[] hardwareAuthToken, String opPackageName) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName); return; @@ -864,55 +795,38 @@ public class FingerprintService extends SystemService { @Override // Binder call public void registerAuthenticators( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { - - // Some HAL might not be started before the system service and will cause the code below - // to wait, and some of the operations below might take a significant amount of time to - // complete (calls to the HALs). To avoid blocking the rest of system server we put - // this on a background thread. - final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, - true /* allowIo */); - thread.start(); - final Handler handler = new Handler(thread.getLooper()); - handler.post(() -> { + mRegistry.registerAll(() -> { + final List<ServiceProvider> providers = new ArrayList<>(); + providers.addAll(getHidlProviders(hidlSensors)); List<String> aidlSensors = new ArrayList<>(); - final String[] instances = - ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR); + final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - registerAuthenticatorsForService(aidlSensors, hidlSensors); + providers.addAll(getAidlProviders( + Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + return providers; }); - thread.quitSafely(); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( IFingerprintAuthenticatorsRegisteredCallback callback) { - if (callback == null) { - Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null"); - return; - } + mRegistry.addAllRegisteredCallback(callback); + } - final boolean registered; - final boolean hasSensorProps; - synchronized (mLock) { - registered = mAuthenticatorsRegisteredCallbacks.register(callback); - hasSensorProps = !mSensorProps.isEmpty(); - } - if (registered && hasSensorProps) { - broadcastAllAuthenticatorsRegistered(); - } else if (!registered) { - Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback"); - } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override + public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { + mBiometricStateCallback.registerBiometricStateListener(listener); } @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId); return; @@ -923,8 +837,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPointerUp(long requestId, int sensorId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId); return; @@ -935,8 +848,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onUiReady(long requestId, int sensorId) { - - final ServiceProvider provider = getProviderForSensor(sensorId); + final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); return; @@ -947,8 +859,7 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) { - - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.setUdfpsOverlayController(controller); } } @@ -956,22 +867,15 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setSidefpsController(@NonNull ISidefpsController controller) { - - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.setSidefpsController(controller); } } - @Override - public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - FingerprintService.this.registerBiometricStateListener(listener); - } - + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void onPowerPressed() { - Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { provider.onPowerPressed(); } } @@ -981,6 +885,7 @@ public class FingerprintService extends SystemService { this(context, BiometricContext.getInstance(context), () -> IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), + () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR), (fqName) -> IFingerprint.Stub.asInterface( Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)))); } @@ -988,61 +893,34 @@ public class FingerprintService extends SystemService { @VisibleForTesting FingerprintService(Context context, BiometricContext biometricContext, - Supplier<IBiometricService> biometricServiceProvider, + Supplier<IBiometricService> biometricServiceSupplier, + Supplier<String[]> aidlInstanceNameSupplier, Function<String, IFingerprint> fingerprintProvider) { super(context); mBiometricContext = biometricContext; - mBiometricServiceSupplier = biometricServiceProvider; + mAidlInstanceNameSupplier = aidlInstanceNameSupplier; mIFingerprintProvider = fingerprintProvider; mAppOps = context.getSystemService(AppOpsManager.class); mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); - mServiceProviders = new ArrayList<>(); - mBiometricStateCallback = new BiometricStateCallback(); - mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>(); - mSensorProps = new ArrayList<>(); + mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); mHandler = new Handler(Looper.getMainLooper()); + mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + mBiometricStateCallback.start(mRegistry.getProviders()); + } + }); } - @VisibleForTesting - void registerAuthenticatorsForService(@NonNull List<String> aidlInstanceNames, + @NonNull + private List<ServiceProvider> getHidlProviders( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { - addHidlProviders(hidlSensors); - addAidlProviders(Utils.filterAvailableHalInstances(getContext(), aidlInstanceNames)); - - final IBiometricService biometricService = mBiometricServiceSupplier.get(); - - // Register each sensor individually with BiometricService - for (ServiceProvider provider : mServiceProviders) { - final List<FingerprintSensorPropertiesInternal> props = - provider.getSensorProperties(); - for (FingerprintSensorPropertiesInternal prop : props) { - final int sensorId = prop.sensorId; - @BiometricManager.Authenticators.Types final int strength = - Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength); - final FingerprintAuthenticator authenticator = new FingerprintAuthenticator( - mServiceWrapper, sensorId); - try { - biometricService.registerAuthenticator(sensorId, TYPE_FINGERPRINT, - strength, authenticator); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId); - } - } - } - - synchronized (mLock) { - for (ServiceProvider provider : mServiceProviders) { - mSensorProps.addAll(provider.getSensorProperties()); - } - } + final List<ServiceProvider> providers = new ArrayList<>(); - broadcastCurrentEnrollmentState(null); // broadcasts to all listeners - broadcastAllAuthenticatorsRegistered(); - } - - private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) { for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) { final Fingerprint21 fingerprint21; if ((Build.IS_USERDEBUG || Build.IS_ENG) @@ -1059,11 +937,16 @@ public class FingerprintService extends SystemService { mBiometricStateCallback, hidlSensor, mHandler, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } - mServiceProviders.add(fingerprint21); + providers.add(fingerprint21); } + + return providers; } - private void addAidlProviders(List<String> instances) { + @NonNull + private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) { + final List<ServiceProvider> providers = new ArrayList<>(); + for (String instance : instances) { final String fqName = IFingerprint.DESCRIPTOR + "/" + instance; final IFingerprint fp = mIFingerprintProvider.apply(fqName); @@ -1075,7 +958,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext); Slog.i(TAG, "Adding AIDL provider: " + fqName); - mServiceProviders.add(provider); + providers.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } @@ -1083,38 +966,8 @@ public class FingerprintService extends SystemService { Slog.e(TAG, "Unable to get declared service: " + fqName); } } - } - // Notifies the callbacks that all of the authenticators have been registered and removes the - // invoked callbacks from the callback list. - private void broadcastAllAuthenticatorsRegistered() { - // Make a local copy of the data so it can be used outside of the synchronized block when - // making Binder calls. - final List<IFingerprintAuthenticatorsRegisteredCallback> callbacks = new ArrayList<>(); - final List<FingerprintSensorPropertiesInternal> props; - synchronized (mLock) { - if (!mSensorProps.isEmpty()) { - props = new ArrayList<>(mSensorProps); - } else { - Slog.e(TAG, "mSensorProps is empty"); - return; - } - final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast(); - for (int i = 0; i < n; ++i) { - final IFingerprintAuthenticatorsRegisteredCallback cb = - mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i); - callbacks.add(cb); - mAuthenticatorsRegisteredCallbacks.unregister(cb); - } - mAuthenticatorsRegisteredCallbacks.finishBroadcast(); - } - for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) { - try { - cb.onAllAuthenticatorsRegistered(props); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e); - } - } + return providers; } @Override @@ -1122,51 +975,9 @@ public class FingerprintService extends SystemService { publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper); } - @Nullable - private ServiceProvider getProviderForSensor(int sensorId) { - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return provider; - } - } - return null; - } - - /** - * For devices with only a single provider, returns that provider. If multiple providers, - * returns the first one. If no providers, returns null. - */ - @Nullable - private Pair<Integer, ServiceProvider> getSingleProvider() { - final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties(); - if (properties.isEmpty()) { - Slog.e(TAG, "No providers found"); - return null; - } - - // Theoretically we can just return the first provider, but maybe this is easier to - // understand. - final int sensorId = properties.get(0).sensorId; - for (ServiceProvider provider : mServiceProviders) { - if (provider.containsSensor(sensorId)) { - return new Pair<>(sensorId, provider); - } - } - - Slog.e(TAG, "Provider not found"); - return null; - } - - @NonNull - private List<FingerprintSensorPropertiesInternal> getSensorProperties() { - synchronized (mLock) { - return mSensorProps; - } - } - @NonNull private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) { - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for getEnrolledFingerprintsDeprecated, caller: " + opPackageName); @@ -1229,7 +1040,7 @@ public class FingerprintService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { Slog.i(TAG, "Sync virtual enrollments"); final int userId = ActivityManager.getCurrentUser(); - for (ServiceProvider provider : mServiceProviders) { + for (ServiceProvider provider : mRegistry.getProviders()) { for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) { provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */, true /* favorHalEnrollments */); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java new file mode 100644 index 000000000000..33810b764f23 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.IBiometricService; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.hardware.fingerprint.IFingerprintService; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceRegistry; + +import java.util.List; +import java.util.function.Supplier; + +/** Registry for {@link IFingerprintService} providers. */ +public class FingerprintServiceRegistry extends BiometricServiceRegistry<ServiceProvider, + FingerprintSensorPropertiesInternal, IFingerprintAuthenticatorsRegisteredCallback> { + + private static final String TAG = "FingerprintServiceRegistry"; + + @NonNull + private final IFingerprintService mService; + + /** Creates a new registry tied to the given service. */ + public FingerprintServiceRegistry(@NonNull IFingerprintService service, + @Nullable Supplier<IBiometricService> biometricSupplier) { + super(biometricSupplier); + mService = service; + } + + @Override + protected void registerService(@NonNull IBiometricService service, + @NonNull FingerprintSensorPropertiesInternal props) { + @BiometricManager.Authenticators.Types final int strength = + Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength); + try { + service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength, + new FingerprintAuthenticator(mService, props.sensorId)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId); + } + } + + @Override + protected void invokeRegisteredCallback( + @NonNull IFingerprintAuthenticatorsRegisteredCallback callback, + @NonNull List<FingerprintSensorPropertiesInternal> allProps) throws RemoteException { + callback.onAllAuthenticatorsRegistered(allProps); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 275d7e445a75..9075e7ec2080 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -28,14 +28,11 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; -import android.util.proto.ProtoOutputStream; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; -import com.android.server.biometrics.sensors.LockoutTracker; -import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.List; /** @@ -59,23 +56,8 @@ import java.util.List; * fail safely. */ @SuppressWarnings("deprecation") -public interface ServiceProvider { - /** - * Checks if the specified sensor is owned by this provider. - */ - boolean containsSensor(int sensorId); - - @NonNull - List<FingerprintSensorPropertiesInternal> getSensorProperties(); - - /** - * Returns the internal properties of the specified sensor, if owned by this provider. - * - * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor. - * @return An object representing the internal properties of the specified sensor. - */ - @Nullable - FingerprintSensorPropertiesInternal getSensorProperties(int sensorId); +public interface ServiceProvider extends + BiometricServiceProvider<FingerprintSensorPropertiesInternal> { void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); @@ -126,16 +108,11 @@ public interface ServiceProvider { void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments); - boolean isHardwareDetected(int sensorId); - void rename(int sensorId, int fingerId, int userId, @NonNull String name); @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId); - @LockoutTracker.LockoutMode - int getLockoutModeForUser(int sensorId, int userId); - /** * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} @@ -143,7 +120,6 @@ public interface ServiceProvider { void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback); - long getAuthenticatorId(int sensorId, int userId); void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); @@ -161,13 +137,6 @@ public interface ServiceProvider { */ void setSidefpsController(@NonNull ISidefpsController controller); - void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, - boolean clearSchedulerBuffer); - - void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd); - - void dumpInternal(int sensorId, @NonNull PrintWriter pw); - @NonNull ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 2dc005206b42..3fe6332fcaa0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -565,6 +565,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFingerprints(sensorId, userId).isEmpty(); + } + + @Override public void scheduleInvalidateAuthenticatorId(int sensorId, int userId, @NonNull IInvalidationCallback callback) { mHandler.post(() -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index ed482f013869..0e6df8e0df77 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -789,6 +789,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override + public boolean hasEnrollments(int sensorId, int userId) { + return !getEnrolledFingerprints(sensorId, userId).isEmpty(); + } + + @Override @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) { return mLockoutTracker.getLockoutModeForUser(userId); } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index a817cea1a674..05d32d1d9023 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -148,6 +148,12 @@ class AutomaticBrightnessController { // The currently accepted nominal ambient light level. private float mAmbientLux; + // The last calculated ambient light level (long time window). + private float mSlowAmbientLux; + + // The last calculated ambient light level (short time window). + private float mFastAmbientLux; + // The last ambient lux value prior to passing the darkening or brightening threshold. private float mPreThresholdLux; @@ -440,6 +446,14 @@ class AutomaticBrightnessController { return mAmbientLux; } + float getSlowAmbientLux() { + return mSlowAmbientLux; + } + + float getFastAmbientLux() { + return mFastAmbientLux; + } + private boolean setDisplayPolicy(int policy) { if (mDisplayPolicy == policy) { return false; @@ -812,20 +826,20 @@ class AutomaticBrightnessController { // proposed ambient light value since the slow value might be sufficiently far enough away // from the fast value to cause a recalculation while its actually just converging on // the fast value still. - float slowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong); - float fastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort); + mSlowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong); + mFastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort); - if ((slowAmbientLux >= mAmbientBrighteningThreshold - && fastAmbientLux >= mAmbientBrighteningThreshold + if ((mSlowAmbientLux >= mAmbientBrighteningThreshold + && mFastAmbientLux >= mAmbientBrighteningThreshold && nextBrightenTransition <= time) - || (slowAmbientLux <= mAmbientDarkeningThreshold - && fastAmbientLux <= mAmbientDarkeningThreshold + || (mSlowAmbientLux <= mAmbientDarkeningThreshold + && mFastAmbientLux <= mAmbientDarkeningThreshold && nextDarkenTransition <= time)) { mPreThresholdLux = mAmbientLux; - setAmbientLux(fastAmbientLux); + setAmbientLux(mFastAmbientLux); if (mLoggingEnabled) { Slog.d(TAG, "updateAmbientLux: " - + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " + + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": " + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", " + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " + "mAmbientLux=" + mAmbientLux); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 6f3a0c516a5b..8807e1970061 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1640,11 +1640,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // brightness cap, RBC state, etc. mTempBrightnessEvent.setTime(System.currentTimeMillis()); mTempBrightnessEvent.setBrightness(brightnessState); + mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); mTempBrightnessEvent.setReason(mBrightnessReason); mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax()); mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode()); mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0)); + mTempBrightnessEvent.setRbcStrength((mCdsi != null && mCdsi.isReduceBrightColorsActivated()) + ? mCdsi.getReduceBrightColorsStrength() : -1); + mTempBrightnessEvent.setPowerFactor( + mPowerRequest.lowPowerMode ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = @@ -1653,9 +1658,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call == BrightnessReason.REASON_TEMPORARY; if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition) || brightnessAdjustmentFlags != 0) { + float lastBrightness = mLastBrightnessEvent.getBrightness(); + mTempBrightnessEvent.setInitialBrightness(lastBrightness); + mTempBrightnessEvent.setFastAmbientLux( + mAutomaticBrightnessController == null + ? -1 : mAutomaticBrightnessController.getFastAmbientLux()); + mTempBrightnessEvent.setSlowAmbientLux( + mAutomaticBrightnessController == null + ? -1 : mAutomaticBrightnessController.getSlowAmbientLux()); + mTempBrightnessEvent.setAutomaticBrightnessEnabled(mPowerRequest.useAutoBrightness); mLastBrightnessEvent.copyFrom(mTempBrightnessEvent); BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent); - // Adjustment flags (and user-set flag) only get added after the equality checks since // they are transient. newEvent.setAdjustmentFlags(brightnessAdjustmentFlags); @@ -1663,6 +1676,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call ? BrightnessEvent.FLAG_USER_SET : 0)); Slog.i(mTag, newEvent.toString(/* includeTime= */ false)); + if (userSetBrightnessChanged) { + logManualBrightnessEvent(newEvent); + } if (mBrightnessEventRingBuffer != null) { mBrightnessEventRingBuffer.append(newEvent); } @@ -2752,6 +2768,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void logManualBrightnessEvent(BrightnessEvent event) { + float hbmMaxNits = + event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF + ? -1f : convertToNits(event.getHbmMax()); + + // thermalCapNits set to -1 if not currently capping max brightness + float thermalCapNits = + event.getThermalMax() == PowerManager.BRIGHTNESS_MAX + ? -1f : convertToNits(event.getThermalMax()); + + FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED, + convertToNits(event.getInitialBrightness()), + convertToNits(event.getBrightness()), + event.getSlowAmbientLux(), + event.getPhysicalDisplayId(), + event.isShortTermModelActive(), + event.getPowerFactor(), + event.getRbcStrength(), + hbmMaxNits, + thermalCapNits, + event.isAutomaticBrightnessEnabled(), + FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL); + } + private final class DisplayControllerHandler extends Handler { DisplayControllerHandler(Looper looper) { super(looper, null, true /*async*/); diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java index d831dbd4bb23..70415a3c81c9 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java @@ -33,17 +33,24 @@ public final class BrightnessEvent { private BrightnessReason mReason = new BrightnessReason(); private int mDisplayId; + private String mPhysicalDisplayId; + private long mTime; private float mLux; + private float mFastAmbientLux; + private float mSlowAmbientLux; private float mPreThresholdLux; - private long mTime; + private float mInitialBrightness; private float mBrightness; private float mRecommendedBrightness; private float mPreThresholdBrightness; + private int mHbmMode; private float mHbmMax; + private int mRbcStrength; private float mThermalMax; - private int mHbmMode; + private float mPowerFactor; private int mFlags; private int mAdjustmentFlags; + private boolean mAutomaticBrightnessEnabled; public BrightnessEvent(BrightnessEvent that) { copyFrom(that); @@ -60,37 +67,59 @@ public final class BrightnessEvent { * @param that BrightnessEvent which is to be copied */ public void copyFrom(BrightnessEvent that) { + mReason.set(that.getReason()); mDisplayId = that.getDisplayId(); + mPhysicalDisplayId = that.getPhysicalDisplayId(); mTime = that.getTime(); + // Lux values mLux = that.getLux(); + mFastAmbientLux = that.getFastAmbientLux(); + mSlowAmbientLux = that.getSlowAmbientLux(); mPreThresholdLux = that.getPreThresholdLux(); + // Brightness values + mInitialBrightness = that.getInitialBrightness(); mBrightness = that.getBrightness(); mRecommendedBrightness = that.getRecommendedBrightness(); mPreThresholdBrightness = that.getPreThresholdBrightness(); + // Different brightness modulations + mHbmMode = that.getHbmMode(); mHbmMax = that.getHbmMax(); + mRbcStrength = that.getRbcStrength(); mThermalMax = that.getThermalMax(); + mPowerFactor = that.getPowerFactor(); mFlags = that.getFlags(); - mHbmMode = that.getHbmMode(); - mReason.set(that.getReason()); mAdjustmentFlags = that.getAdjustmentFlags(); + // Auto-brightness setting + mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled(); } /** * A utility to reset the BrightnessEvent to default values */ public void reset() { + mReason.set(null); mTime = SystemClock.uptimeMillis(); - mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mRecommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mPhysicalDisplayId = ""; + // Lux values mLux = 0; + mFastAmbientLux = 0; + mSlowAmbientLux = 0; mPreThresholdLux = 0; + // Brightness values + mInitialBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mRecommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + // Different brightness modulations + mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; mHbmMax = PowerManager.BRIGHTNESS_MAX; + mRbcStrength = 0; mThermalMax = PowerManager.BRIGHTNESS_MAX; + mPowerFactor = 1f; mFlags = 0; - mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; - mReason.set(null); mAdjustmentFlags = 0; + // Auto-brightness setting + mAutomaticBrightnessEnabled = true; } /** @@ -104,23 +133,34 @@ public final class BrightnessEvent { public boolean equalsMainData(BrightnessEvent that) { // This equals comparison purposefully ignores time since it is regularly changing and // we don't want to log a brightness event just because the time changed. - return mDisplayId == that.mDisplayId + return mReason.equals(that.mReason) + && mDisplayId == that.mDisplayId + && mPhysicalDisplayId.equals(that.mPhysicalDisplayId) + && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux) + && Float.floatToRawIntBits(mFastAmbientLux) + == Float.floatToRawIntBits(that.mFastAmbientLux) + && Float.floatToRawIntBits(mSlowAmbientLux) + == Float.floatToRawIntBits(that.mSlowAmbientLux) + && Float.floatToRawIntBits(mPreThresholdLux) + == Float.floatToRawIntBits(that.mPreThresholdLux) + && Float.floatToRawIntBits(mInitialBrightness) + == Float.floatToRawIntBits(that.mInitialBrightness) && Float.floatToRawIntBits(mBrightness) == Float.floatToRawIntBits(that.mBrightness) && Float.floatToRawIntBits(mRecommendedBrightness) == Float.floatToRawIntBits(that.mRecommendedBrightness) && Float.floatToRawIntBits(mPreThresholdBrightness) == Float.floatToRawIntBits(that.mPreThresholdBrightness) - && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux) - && Float.floatToRawIntBits(mPreThresholdLux) - == Float.floatToRawIntBits(that.mPreThresholdLux) - && Float.floatToRawIntBits(mHbmMax) == Float.floatToRawIntBits(that.mHbmMax) && mHbmMode == that.mHbmMode + && Float.floatToRawIntBits(mHbmMax) == Float.floatToRawIntBits(that.mHbmMax) + && mRbcStrength == that.mRbcStrength && Float.floatToRawIntBits(mThermalMax) == Float.floatToRawIntBits(that.mThermalMax) + && Float.floatToRawIntBits(mPowerFactor) + == Float.floatToRawIntBits(that.mPowerFactor) && mFlags == that.mFlags && mAdjustmentFlags == that.mAdjustmentFlags - && mReason.equals(that.mReason); + && mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled; } /** @@ -133,16 +173,23 @@ public final class BrightnessEvent { return (includeTime ? TimeUtils.formatForLogging(mTime) + " - " : "") + "BrightnessEvent: " + "disp=" + mDisplayId + + ", physDisp=" + mPhysicalDisplayId + ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "") + + ", initBrt=" + mInitialBrightness + ", rcmdBrt=" + mRecommendedBrightness + ", preBrt=" + mPreThresholdBrightness + ", lux=" + mLux + + ", fastLux=" + mFastAmbientLux + + ", slowLux=" + mSlowAmbientLux + ", preLux=" + mPreThresholdLux + ", hbmMax=" + mHbmMax + ", hbmMode=" + BrightnessInfo.hbmToString(mHbmMode) + + ", rbcStrength=" + mRbcStrength + ", thrmMax=" + mThermalMax + + ", powerFactor=" + mPowerFactor + ", flags=" + flagsToString() - + ", reason=" + mReason.toString(mAdjustmentFlags); + + ", reason=" + mReason.toString(mAdjustmentFlags) + + ", autoBrightness=" + mAutomaticBrightnessEnabled; } @Override @@ -150,12 +197,20 @@ public final class BrightnessEvent { return toString(/* includeTime */ true); } + public BrightnessReason getReason() { + return mReason; + } + public void setReason(BrightnessReason reason) { this.mReason = reason; } - public BrightnessReason getReason() { - return mReason; + public long getTime() { + return mTime; + } + + public void setTime(long time) { + this.mTime = time; } public int getDisplayId() { @@ -166,6 +221,14 @@ public final class BrightnessEvent { this.mDisplayId = displayId; } + public String getPhysicalDisplayId() { + return mPhysicalDisplayId; + } + + public void setPhysicalDisplayId(String mPhysicalDisplayId) { + this.mPhysicalDisplayId = mPhysicalDisplayId; + } + public float getLux() { return mLux; } @@ -174,6 +237,22 @@ public final class BrightnessEvent { this.mLux = lux; } + public float getFastAmbientLux() { + return mFastAmbientLux; + } + + public void setFastAmbientLux(float mFastAmbientLux) { + this.mFastAmbientLux = mFastAmbientLux; + } + + public float getSlowAmbientLux() { + return mSlowAmbientLux; + } + + public void setSlowAmbientLux(float mSlowAmbientLux) { + this.mSlowAmbientLux = mSlowAmbientLux; + } + public float getPreThresholdLux() { return mPreThresholdLux; } @@ -182,12 +261,12 @@ public final class BrightnessEvent { this.mPreThresholdLux = preThresholdLux; } - public long getTime() { - return mTime; + public float getInitialBrightness() { + return mInitialBrightness; } - public void setTime(long time) { - this.mTime = time; + public void setInitialBrightness(float mInitialBrightness) { + this.mInitialBrightness = mInitialBrightness; } public float getBrightness() { @@ -214,6 +293,14 @@ public final class BrightnessEvent { this.mPreThresholdBrightness = preThresholdBrightness; } + public int getHbmMode() { + return mHbmMode; + } + + public void setHbmMode(int hbmMode) { + this.mHbmMode = hbmMode; + } + public float getHbmMax() { return mHbmMax; } @@ -222,6 +309,14 @@ public final class BrightnessEvent { this.mHbmMax = hbmMax; } + public int getRbcStrength() { + return mRbcStrength; + } + + public void setRbcStrength(int mRbcStrength) { + this.mRbcStrength = mRbcStrength; + } + public float getThermalMax() { return mThermalMax; } @@ -230,12 +325,12 @@ public final class BrightnessEvent { this.mThermalMax = thermalMax; } - public int getHbmMode() { - return mHbmMode; + public float getPowerFactor() { + return mPowerFactor; } - public void setHbmMode(int hbmMode) { - this.mHbmMode = hbmMode; + public void setPowerFactor(float mPowerFactor) { + this.mPowerFactor = mPowerFactor; } public int getFlags() { @@ -246,6 +341,10 @@ public final class BrightnessEvent { this.mFlags = flags; } + public boolean isShortTermModelActive() { + return (mFlags & FLAG_USER_SET) != 0; + } + public int getAdjustmentFlags() { return mAdjustmentFlags; } @@ -254,6 +353,14 @@ public final class BrightnessEvent { this.mAdjustmentFlags = adjustmentFlags; } + public boolean isAutomaticBrightnessEnabled() { + return mAutomaticBrightnessEnabled; + } + + public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) { + this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled; + } + private String flagsToString() { return ((mFlags & FLAG_USER_SET) != 0 ? "user_set " : "") + ((mFlags & FLAG_RBC) != 0 ? "rbc " : "") diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 6e304163995b..366dfd1efd29 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -1515,6 +1515,10 @@ public final class ColorDisplayService extends SystemService { return mReduceBrightColorsTintController.isActivated(); } + public int getReduceBrightColorsStrength() { + return mReduceBrightColorsTintController.getStrength(); + } + /** * Gets the computed brightness, in nits, when the reduce bright colors feature is applied * at the current strength. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d48acb18722a..23f437392d2b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -910,11 +910,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final String mImeControlTargetName; @Nullable final String mImeTargetNameFromWm; + @Nullable + final String mImeSurfaceParentName; Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName, @SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason, boolean inFullscreenMode, String requestWindowName, - @Nullable String imeControlTargetName, @Nullable String imeTargetName) { + @Nullable String imeControlTargetName, @Nullable String imeTargetName, + @Nullable String imeSurfaceParentName) { mClientState = client; mEditorInfo = editorInfo; mFocusedWindowName = focusedWindowName; @@ -926,6 +929,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mRequestWindowName = requestWindowName; mImeControlTargetName = imeControlTargetName; mImeTargetNameFromWm = imeTargetName; + mImeSurfaceParentName = imeSurfaceParentName; } } @@ -972,6 +976,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub pw.println(" imeTargetNameFromWm=" + entry.mImeTargetNameFromWm); pw.print(prefix); + pw.println(" imeSurfaceParentName=" + entry.mImeSurfaceParentName); + + pw.print(prefix); pw.print(" editorInfo: "); pw.print(" inputType=" + entry.mEditorInfo.inputType); pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions); @@ -4676,7 +4683,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry( mCurFocusedWindowClient, mCurEditorInfo, info.focusedWindowName, mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode, - info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName)); + info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName, + info.imeSurfaceParentName)); } @BinderThread diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index e27cbeaab139..bfa8af957208 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -927,8 +927,9 @@ class MediaRouter2ServiceImpl { routerRecord.mUserRecord.mHandler, routerRecord, manager)); } - userRecord.mHandler.sendMessage(obtainMessage(UserHandler::notifyRoutesToManager, - userRecord.mHandler, manager)); + userRecord.mHandler.sendMessage( + obtainMessage( + UserHandler::notifyInitialRoutesToManager, userRecord.mHandler, manager)); } private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) { @@ -1311,6 +1312,36 @@ class MediaRouter2ServiceImpl { new CopyOnWriteArrayList<>(); private final Map<String, RouterRecord> mSessionToRouterMap = new ArrayMap<>(); + /** + * Latest list of routes sent to privileged {@link android.media.MediaRouter2 routers} and + * {@link android.media.MediaRouter2Manager managers}. + * + * <p>Privileged routers are instances of {@link android.media.MediaRouter2 MediaRouter2} + * that have {@code MODIFY_AUDIO_ROUTING} permission. + * + * <p>This list contains all routes exposed by route providers. This includes routes from + * both system route providers and user route providers. + * + * <p>See {@link #getRouters(boolean hasModifyAudioRoutingPermission)}. + */ + private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters = + new ArrayMap<>(); + + /** + * Latest list of routes sent to non-privileged {@link android.media.MediaRouter2 routers}. + * + * <p>Non-privileged routers are instances of {@link android.media.MediaRouter2 + * MediaRouter2} that do <i><b>not</b></i> have {@code MODIFY_AUDIO_ROUTING} permission. + * + * <p>This list contains all routes exposed by user route providers. It might also include + * the current default route from {@link #mSystemProvider} to expose local route updates + * (e.g. volume changes) to non-privileged routers. + * + * <p>See {@link SystemMediaRoute2Provider#mDefaultRoute}. + */ + private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToNonPrivilegedRouters = + new ArrayMap<>(); + private boolean mRunning; // TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler. @@ -1425,91 +1456,182 @@ class MediaRouter2ServiceImpl { } private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { - int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo(); + + int providerInfoIndex = + indexOfRouteProviderInfoByUniqueId(provider.getUniqueId(), mLastProviderInfos); + MediaRoute2ProviderInfo prevInfo = - (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex); - if (Objects.equals(prevInfo, currentInfo)) return; + providerInfoIndex == -1 ? null : mLastProviderInfos.get(providerInfoIndex); + + // Ignore if no changes + if (Objects.equals(prevInfo, currentInfo)) { + return; + } + + boolean hasAddedOrModifiedRoutes = false; + boolean hasRemovedRoutes = false; + + boolean isSystemProvider = provider.mIsSystemRouteProvider; - List<MediaRoute2Info> addedRoutes = new ArrayList<>(); - List<MediaRoute2Info> removedRoutes = new ArrayList<>(); - List<MediaRoute2Info> changedRoutes = new ArrayList<>(); if (prevInfo == null) { + // Provider is being added. mLastProviderInfos.add(currentInfo); - addedRoutes.addAll(currentInfo.getRoutes()); + addToRoutesMap(currentInfo.getRoutes(), isSystemProvider); + // Check if new provider exposes routes. + hasAddedOrModifiedRoutes = !currentInfo.getRoutes().isEmpty(); } else if (currentInfo == null) { + // Provider is being removed. + hasRemovedRoutes = true; mLastProviderInfos.remove(prevInfo); - removedRoutes.addAll(prevInfo.getRoutes()); + removeFromRoutesMap(prevInfo.getRoutes(), isSystemProvider); } else { + // Provider is being updated. mLastProviderInfos.set(providerInfoIndex, currentInfo); - final Collection<MediaRoute2Info> prevRoutes = prevInfo.getRoutes(); final Collection<MediaRoute2Info> currentRoutes = currentInfo.getRoutes(); + // Checking for individual routes. for (MediaRoute2Info route : currentRoutes) { if (!route.isValid()) { - Slog.w(TAG, "onProviderStateChangedOnHandler: Ignoring invalid route : " - + route); + Slog.w( + TAG, + "onProviderStateChangedOnHandler: Ignoring invalid route : " + + route); continue; } + MediaRoute2Info prevRoute = prevInfo.getRoute(route.getOriginalId()); - if (prevRoute == null) { - addedRoutes.add(route); - } else if (!Objects.equals(prevRoute, route)) { - changedRoutes.add(route); + if (prevRoute == null || !Objects.equals(prevRoute, route)) { + hasAddedOrModifiedRoutes = true; + mLastNotifiedRoutesToPrivilegedRouters.put(route.getId(), route); + if (!isSystemProvider) { + mLastNotifiedRoutesToNonPrivilegedRouters.put(route.getId(), route); + } } } + // Checking for individual removals for (MediaRoute2Info prevRoute : prevInfo.getRoutes()) { if (currentInfo.getRoute(prevRoute.getOriginalId()) == null) { - removedRoutes.add(prevRoute); + hasRemovedRoutes = true; + mLastNotifiedRoutesToPrivilegedRouters.remove(prevRoute.getId()); + if (!isSystemProvider) { + mLastNotifiedRoutesToNonPrivilegedRouters.remove(prevRoute.getId()); + } } } } + dispatchUpdates( + hasAddedOrModifiedRoutes, + hasRemovedRoutes, + isSystemProvider, + mSystemProvider.getDefaultRoute()); + } + + /** + * Adds provided routes to {@link #mLastNotifiedRoutesToPrivilegedRouters}. Also adds them + * to {@link #mLastNotifiedRoutesToNonPrivilegedRouters} if they were provided by a + * non-system route provider. Overwrites any route with matching id that already exists. + * + * @param routes list of routes to be added. + * @param isSystemRoutes indicates whether routes come from a system route provider. + */ + private void addToRoutesMap( + @NonNull Collection<MediaRoute2Info> routes, boolean isSystemRoutes) { + for (MediaRoute2Info route : routes) { + if (!isSystemRoutes) { + mLastNotifiedRoutesToNonPrivilegedRouters.put(route.getId(), route); + } + mLastNotifiedRoutesToPrivilegedRouters.put(route.getId(), route); + } + } + + /** + * Removes provided routes from {@link #mLastNotifiedRoutesToPrivilegedRouters}. Also + * removes them from {@link #mLastNotifiedRoutesToNonPrivilegedRouters} if they were + * provided by a non-system route provider. + * + * @param routes list of routes to be removed. + * @param isSystemRoutes whether routes come from a system route provider. + */ + private void removeFromRoutesMap( + @NonNull Collection<MediaRoute2Info> routes, boolean isSystemRoutes) { + for (MediaRoute2Info route : routes) { + if (!isSystemRoutes) { + mLastNotifiedRoutesToNonPrivilegedRouters.remove(route.getId()); + } + mLastNotifiedRoutesToPrivilegedRouters.remove(route.getId()); + } + } + + /** + * Dispatches the latest route updates in {@link #mLastNotifiedRoutesToPrivilegedRouters} + * and {@link #mLastNotifiedRoutesToNonPrivilegedRouters} to registered {@link + * android.media.MediaRouter2 routers} and {@link MediaRouter2Manager managers} after a call + * to {@link #onProviderStateChangedOnHandler(MediaRoute2Provider)}. Ignores if no changes + * were made. + * + * @param hasAddedOrModifiedRoutes whether routes were added or modified. + * @param hasRemovedRoutes whether routes were removed. + * @param isSystemProvider whether the latest update was caused by a system provider. + * @param defaultRoute the current default route in {@link #mSystemProvider}. + */ + private void dispatchUpdates( + boolean hasAddedOrModifiedRoutes, + boolean hasRemovedRoutes, + boolean isSystemProvider, + MediaRoute2Info defaultRoute) { + + // Ignore if no changes. + if (!hasAddedOrModifiedRoutes && !hasRemovedRoutes) { + return; + } + List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true); List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false); List<IMediaRouter2Manager> managers = getManagers(); - List<MediaRoute2Info> defaultRoute = new ArrayList<>(); - defaultRoute.add(mSystemProvider.getDefaultRoute()); - - if (addedRoutes.size() > 0) { - notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes); - if (!provider.mIsSystemRouteProvider) { - notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, - addedRoutes); - } else if (prevInfo == null) { - notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, - defaultRoute); - } // 'else' is handled as changed routes - notifyRoutesAddedToManagers(managers, addedRoutes); - } - if (removedRoutes.size() > 0) { - notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission, - removedRoutes); - if (!provider.mIsSystemRouteProvider) { - notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission, - removedRoutes); - } - notifyRoutesRemovedToManagers(managers, removedRoutes); - } - if (changedRoutes.size() > 0) { - notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission, - changedRoutes); - if (!provider.mIsSystemRouteProvider) { - notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, - changedRoutes); - } else if (prevInfo != null) { - notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, - defaultRoute); - } // 'else' is handled as added routes - notifyRoutesChangedToManagers(managers, changedRoutes); - } - } - - private int getLastProviderInfoIndex(@NonNull String providerId) { - for (int i = 0; i < mLastProviderInfos.size(); i++) { - MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i); - if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) { + + // Managers receive all provider updates with all routes. + notifyRoutesUpdatedToManagers( + managers, new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); + + // Routers with modify audio permission (usually system routers) receive all provider + // updates with all routes. + notifyRoutesUpdatedToRouters( + routersWithModifyAudioRoutingPermission, + new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); + + if (!isSystemProvider) { + // Regular routers receive updates from all non-system providers with all non-system + // routes. + notifyRoutesUpdatedToRouters( + routersWithoutModifyAudioRoutingPermission, + new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values())); + } else if (hasAddedOrModifiedRoutes) { + // On system provider updates, regular routers receive the updated default route. + // This is the only system route they should receive. + mLastNotifiedRoutesToNonPrivilegedRouters.put(defaultRoute.getId(), defaultRoute); + notifyRoutesUpdatedToRouters( + routersWithoutModifyAudioRoutingPermission, + new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values())); + } + } + + /** + * Returns the index of the first element in {@code lastProviderInfos} that matches the + * specified unique id. + * + * @param uniqueId unique id of {@link MediaRoute2ProviderInfo} to be found. + * @param lastProviderInfos list of {@link MediaRoute2ProviderInfo}. + * @return index of found element, or -1 if not found. + */ + private static int indexOfRouteProviderInfoByUniqueId( + @NonNull String uniqueId, + @NonNull List<MediaRoute2ProviderInfo> lastProviderInfos) { + for (int i = 0; i < lastProviderInfos.size(); i++) { + MediaRoute2ProviderInfo providerInfo = lastProviderInfos.get(i); + if (TextUtils.equals(providerInfo.getUniqueId(), uniqueId)) { return i; } } @@ -1989,41 +2111,19 @@ class MediaRouter2ServiceImpl { } } - private void notifyRoutesAddedToRouters(@NonNull List<IMediaRouter2> routers, - @NonNull List<MediaRoute2Info> routes) { - for (IMediaRouter2 router : routers) { - try { - router.notifyRoutesAdded(routes); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify routes added. Router probably died.", ex); - } - } - } - - private void notifyRoutesRemovedToRouters(@NonNull List<IMediaRouter2> routers, - @NonNull List<MediaRoute2Info> routes) { - for (IMediaRouter2 router : routers) { - try { - router.notifyRoutesRemoved(routes); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify routes removed. Router probably died.", ex); - } - } - } - - private void notifyRoutesChangedToRouters(@NonNull List<IMediaRouter2> routers, - @NonNull List<MediaRoute2Info> routes) { + private void notifyRoutesUpdatedToRouters( + @NonNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes) { for (IMediaRouter2 router : routers) { try { - router.notifyRoutesChanged(routes); + router.notifyRoutesUpdated(routes); } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify routes changed. Router probably died.", ex); + Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex); } } } - private void notifySessionInfoChangedToRouters(@NonNull List<IMediaRouter2> routers, - @NonNull RoutingSessionInfo sessionInfo) { + private void notifySessionInfoChangedToRouters( + @NonNull List<IMediaRouter2> routers, @NonNull RoutingSessionInfo sessionInfo) { for (IMediaRouter2 router : routers) { try { router.notifySessionInfoChanged(sessionInfo); @@ -2033,48 +2133,31 @@ class MediaRouter2ServiceImpl { } } - private void notifyRoutesToManager(@NonNull IMediaRouter2Manager manager) { - List<MediaRoute2Info> routes = new ArrayList<>(); - for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) { - routes.addAll(providerInfo.getRoutes()); - } - if (routes.size() == 0) { + /** + * Notifies {@code manager} with all known routes. This only happens once after {@code + * manager} is registered through {@link #registerManager(IMediaRouter2Manager, String) + * registerManager()}. + * + * @param manager {@link IMediaRouter2Manager} to be notified. + */ + private void notifyInitialRoutesToManager(@NonNull IMediaRouter2Manager manager) { + if (mLastNotifiedRoutesToPrivilegedRouters.isEmpty()) { return; } try { - manager.notifyRoutesAdded(routes); + manager.notifyRoutesUpdated( + new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values())); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex); } } - private void notifyRoutesAddedToManagers(@NonNull List<IMediaRouter2Manager> managers, - @NonNull List<MediaRoute2Info> routes) { - for (IMediaRouter2Manager manager : managers) { - try { - manager.notifyRoutesAdded(routes); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify routes added. Manager probably died.", ex); - } - } - } - - private void notifyRoutesRemovedToManagers(@NonNull List<IMediaRouter2Manager> managers, - @NonNull List<MediaRoute2Info> routes) { - for (IMediaRouter2Manager manager : managers) { - try { - manager.notifyRoutesRemoved(routes); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify routes removed. Manager probably died.", ex); - } - } - } - - private void notifyRoutesChangedToManagers(@NonNull List<IMediaRouter2Manager> managers, + private void notifyRoutesUpdatedToManagers( + @NonNull List<IMediaRouter2Manager> managers, @NonNull List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { - manager.notifyRoutesChanged(routes); + manager.notifyRoutesUpdated(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex); } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index a21919cdb960..d48af213dfc7 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -1,5 +1,6 @@ package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.processStateAmToProto; @@ -274,6 +275,8 @@ class ActivityMetricsLogger { final boolean mProcessRunning; /** whether the process of the launching activity didn't have any active activity. */ final boolean mProcessSwitch; + /** The process state of the launching activity prior to the launch */ + final int mProcessState; /** Whether the last launched activity has reported drawn. */ boolean mIsDrawn; /** The latest activity to have been launched. */ @@ -309,8 +312,8 @@ class ActivityMetricsLogger { @Nullable static TransitionInfo create(@NonNull ActivityRecord r, @NonNull LaunchingState launchingState, @Nullable ActivityOptions options, - boolean processRunning, boolean processSwitch, boolean newActivityCreated, - int startResult) { + boolean processRunning, boolean processSwitch, int processState, + boolean newActivityCreated, int startResult) { if (startResult != START_SUCCESS && startResult != START_TASK_TO_FRONT) { return null; } @@ -325,18 +328,19 @@ class ActivityMetricsLogger { transitionType = TYPE_TRANSITION_COLD_LAUNCH; } return new TransitionInfo(r, launchingState, options, transitionType, processRunning, - processSwitch); + processSwitch, processState); } /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */ private TransitionInfo(ActivityRecord r, LaunchingState launchingState, ActivityOptions options, int transitionType, boolean processRunning, - boolean processSwitch) { + boolean processSwitch, int processState) { mLaunchingState = launchingState; mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs; mTransitionType = transitionType; mProcessRunning = processRunning; mProcessSwitch = processSwitch; + mProcessState = processState; mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs; setLatestLaunchedActivity(r); // The launching state can be reused by consecutive launch. Its original association @@ -640,12 +644,16 @@ class ActivityMetricsLogger { // interesting. final boolean processSwitch = !processRunning || !processRecord.hasStartedActivity(launchedActivity); + final int processState = processRunning + ? processRecord.getCurrentProcState() + : PROCESS_STATE_NONEXISTENT; final TransitionInfo info = launchingState.mAssociatedTransitionInfo; if (DEBUG_METRICS) { Slog.i(TAG, "notifyActivityLaunched" + " resultCode=" + resultCode + " launchedActivity=" + launchedActivity + " processRunning=" + processRunning + " processSwitch=" + processSwitch + + " processState=" + processState + " newActivityCreated=" + newActivityCreated + " info=" + info); } @@ -681,7 +689,8 @@ class ActivityMetricsLogger { } final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState, - options, processRunning, processSwitch, newActivityCreated, resultCode); + options, processRunning, processSwitch, processState, newActivityCreated, + resultCode); if (newInfo == null) { abort(launchingState, "unrecognized launch"); return; @@ -996,8 +1005,9 @@ class ActivityMetricsLogger { final long timestamp = info.mTransitionStartTimeNs; final long uptime = info.mTransitionDeviceUptimeMs; final int transitionDelay = info.mCurrentTransitionDelayMs; + final int processState = info.mProcessState; mLoggerHandler.post(() -> logAppTransition( - timestamp, uptime, transitionDelay, infoSnapshot, isHibernating)); + timestamp, uptime, transitionDelay, infoSnapshot, isHibernating, processState)); } mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot)); if (info.mPendingFullyDrawn != null) { @@ -1009,7 +1019,8 @@ class ActivityMetricsLogger { // This gets called on another thread without holding the activity manager lock. private void logAppTransition(long transitionStartTimeNs, long transitionDeviceUptimeMs, - int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating) { + int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating, + int processState) { final LogMaker builder = new LogMaker(APP_TRANSITION); builder.setPackageName(info.packageName); builder.setType(info.type); @@ -1075,7 +1086,8 @@ class ActivityMetricsLogger { isIncremental, isLoading, info.launchedActivityName.hashCode(), - TimeUnit.NANOSECONDS.toMillis(transitionStartTimeNs)); + TimeUnit.NANOSECONDS.toMillis(transitionStartTimeNs), + processState); if (DEBUG_METRICS) { Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)", diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2bb179327cd1..c64e5255c78c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -78,6 +78,11 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; @@ -8751,15 +8756,35 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Returns the min aspect ratio of this activity. */ - private float getMinAspectRatio() { - return info.getMinAspectRatio(getRequestedOrientation()); + float getMinAspectRatio() { + if (info.applicationInfo == null || !info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || ( + info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY) + && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation()))) { + return info.getMinAspectRatio(); + } + + if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) { + return Math.max(mLetterboxUiController.getSplitScreenAspectRatio(), + info.getMinAspectRatio()); + } + + if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) { + return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + info.getMinAspectRatio()); + } + + if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) { + return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, + info.getMinAspectRatio()); + } + return info.getMinAspectRatio(); } /** * Returns true if the activity has maximum or minimum aspect ratio. */ private boolean hasFixedAspectRatio() { - return info.hasFixedAspectRatio(getRequestedOrientation()); + return info.getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c8692ca81b2e..fa3fc9ffb69c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -53,7 +53,6 @@ import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; -import static android.view.ViewRootImpl.LOCAL_LAYOUT; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; @@ -950,7 +949,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy() .getPreferredModeId(w); - if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0 + if (w.isFocused() && mTmpApplySurfaceChangesTransactionState.preferredModeId == 0 && preferredModeId != 0) { mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId; } @@ -2710,25 +2709,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mCurrentPrivacyIndicatorBounds = mCurrentPrivacyIndicatorBounds.updateStaticBounds(staticBounds); if (!Objects.equals(oldBounds, mCurrentPrivacyIndicatorBounds)) { - updateDisplayFrames(false /* insetsSourceMayChange */, true /* notifyInsetsChange */); + updateDisplayFrames(true /* notifyInsetsChange */); } } void onDisplayInfoChanged() { - updateDisplayFrames(LOCAL_LAYOUT, LOCAL_LAYOUT); + updateDisplayFrames(false /* notifyInsetsChange */); mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp(); mInputMonitor.layoutInputConsumers(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); mDisplayPolicy.onDisplayInfoChanged(mDisplayInfo); } - private void updateDisplayFrames(boolean insetsSourceMayChange, boolean notifyInsetsChange) { + private void updateDisplayFrames(boolean notifyInsetsChange) { if (mDisplayFrames.update(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation), calculateRoundedCornersForRotation(mDisplayInfo.rotation), calculatePrivacyIndicatorBoundsForRotation(mDisplayInfo.rotation))) { - if (insetsSourceMayChange) { - mDisplayPolicy.updateInsetsSourceFramesExceptIme(mDisplayFrames); - } mInsetsStateController.onDisplayFramesUpdated(notifyInsetsChange); } } @@ -4416,13 +4412,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting InsetsControlTarget computeImeControlTarget() { + if (mImeInputTarget == null) { + // A special case that if there is no IME input target while the IME is being killed, + // in case seeing unexpected IME surface visibility change when delivering the IME leash + // to the remote insets target during the IME restarting, but the focus window is not in + // multi-windowing mode, return null target until the next input target updated. + return null; + } + + final WindowState imeInputTarget = mImeInputTarget.getWindowState(); if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null - || (mImeInputTarget != null - && getImeHostOrFallback(mImeInputTarget.getWindowState()) - == mRemoteInsetsControlTarget)) { + || getImeHostOrFallback(imeInputTarget) == mRemoteInsetsControlTarget) { return mRemoteInsetsControlTarget; } else { - return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null; + return imeInputTarget; } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 07694065cbd7..5221072f696d 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1576,19 +1576,6 @@ public class DisplayPolicy { } } - void updateInsetsSourceFramesExceptIme(DisplayFrames displayFrames) { - sTmpClientFrames.attachedFrame = null; - for (int i = mInsetsSourceWindowsExceptIme.size() - 1; i >= 0; i--) { - final WindowState win = mInsetsSourceWindowsExceptIme.valueAt(i); - mWindowLayout.computeFrames(win.mAttrs.forRotation(displayFrames.mRotation), - displayFrames.mInsetsState, displayFrames.mDisplayCutoutSafe, - displayFrames.mUnrestricted, win.getWindowingMode(), UNSPECIFIED_LENGTH, - UNSPECIFIED_LENGTH, win.getRequestedVisibilities(), win.mGlobalScale, - sTmpClientFrames); - win.updateSourceFrame(sTmpClientFrames.frame); - } - } - void onDisplayInfoChanged(DisplayInfo info) { mSystemGestures.onDisplayInfoChanged(info); } diff --git a/services/core/java/com/android/server/wm/LaunchParamsUtil.java b/services/core/java/com/android/server/wm/LaunchParamsUtil.java index a618f7c79238..a0e22e7d9dc5 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsUtil.java +++ b/services/core/java/com/android/server/wm/LaunchParamsUtil.java @@ -63,7 +63,7 @@ class LaunchParamsUtil { * Calculate the default size for a freeform environment. |defaultSize| is used as the default * DP size, but if this is null, the portrait phone size is used. */ - static Size getDefaultFreeformSize(@NonNull ActivityInfo info, + static Size getDefaultFreeformSize(@NonNull ActivityRecord activityRecord, @NonNull TaskDisplayArea displayArea, @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect stableBounds) { @@ -98,8 +98,8 @@ class LaunchParamsUtil { final float aspectRatio = (float) Math.max(width, height) / (float) Math.min(width, height); // Aspect ratio requirements. - final float minAspectRatio = info.getMinAspectRatio(orientation); - final float maxAspectRatio = info.getMaxAspectRatio(); + final float minAspectRatio = activityRecord.getMinAspectRatio(); + final float maxAspectRatio = activityRecord.info.getMaxAspectRatio(); // Adjust the width and height to the aspect ratio requirements. int adjWidth = width; diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index ec9ee29679a4..c8ed602b0f1d 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -257,6 +257,10 @@ final class LetterboxUiController { : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); } + return getSplitScreenAspectRatio(); + } + + float getSplitScreenAspectRatio() { int dividerWindowWidth = getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness); int dividerInsets = diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 0128c187bbdd..fb68fe666c0b 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -242,16 +242,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public int relayout(IWindow window, WindowManager.LayoutParams attrs, - int requestedWidth, int requestedHeight, int viewFlags, int flags, - ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, - SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) { + int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, + int lastSyncSeqId, ClientWindowFrames outFrames, + MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, + Bundle outSyncSeqIdBundle) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); int res = mService.relayoutWindow(this, window, attrs, - requestedWidth, requestedHeight, viewFlags, flags, - outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, + requestedWidth, requestedHeight, viewFlags, flags, seq, + lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls, outSyncSeqIdBundle); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " @@ -260,6 +261,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override + public void relayoutAsync(IWindow window, WindowManager.LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, + int lastSyncSeqId) { + relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq, + lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */, + null /* outSurfaceControl */, null /* outInsetsState */, + null /* outActiveControls */, null /* outSyncIdBundle */); + } + + @Override public boolean outOfMemory(IWindow window) { return mService.outOfMemoryWindow(this, window); } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 7b0337d52da2..136209417f39 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -746,7 +746,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // First we get the default size we want. displayArea.getStableRect(mTmpStableBounds); - final Size defaultSize = LaunchParamsUtil.getDefaultFreeformSize(root.info, displayArea, + final Size defaultSize = LaunchParamsUtil.getDefaultFreeformSize(root, displayArea, layout, orientation, mTmpStableBounds); mTmpBounds.set(0, 0, defaultSize.getWidth(), defaultSize.getHeight()); if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index da5f5d0e29fd..a71c3866ba38 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -810,12 +810,17 @@ public abstract class WindowManagerInternal { */ public final String imeLayerTargetName; + /** The surface parent of the IME container. */ + public final String imeSurfaceParentName; + public ImeTargetInfo(String focusedWindowName, String requestWindowName, - String imeControlTargetName, String imeLayerTargetName) { + String imeControlTargetName, String imeLayerTargetName, + String imeSurfaceParentName) { this.focusedWindowName = focusedWindowName; this.requestWindowName = requestWindowName; this.imeControlTargetName = imeControlTargetName; this.imeLayerTargetName = imeLayerTargetName; + this.imeSurfaceParentName = imeSurfaceParentName; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index fd54f78b22fe..6544f821f13c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2248,11 +2248,14 @@ public class WindowManagerService extends IWindowManager.Stub } public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, - int requestedWidth, int requestedHeight, int viewVisibility, int flags, - ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, - SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) { - Arrays.fill(outActiveControls, null); + int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq, + int lastSyncSeqId, ClientWindowFrames outFrames, + MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl, + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, + Bundle outSyncIdBundle) { + if (outActiveControls != null) { + Arrays.fill(outActiveControls, null); + } int result = 0; boolean configChanged; final int pid = Binder.getCallingPid(); @@ -2263,8 +2266,15 @@ public class WindowManagerService extends IWindowManager.Stub if (win == null) { return 0; } + if (win.mRelayoutSeq < seq) { + win.mRelayoutSeq = seq; + } else if (win.mRelayoutSeq > seq) { + return 0; + } - if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= win.mLastSeqIdSentToRelayout) { + if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) { + // The client has reported the sync draw, but we haven't finished it yet. + // Don't let the client perform a non-sync draw at this time. result |= RELAYOUT_RES_CANCEL_AND_REDRAW; } @@ -2427,7 +2437,7 @@ public class WindowManagerService extends IWindowManager.Stub // Create surfaceControl before surface placement otherwise layout will be skipped // (because WS.isGoneForLayout() is true when there is no surface. - if (shouldRelayout) { + if (shouldRelayout && outSurfaceControl != null) { try { result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { @@ -2466,22 +2476,25 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mEnterAnimationPending = false; winAnimator.mEnteringAnimation = false; - if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) { - // We already told the client to go invisible, but the message may not be - // handled yet, or it might want to draw a last frame. If we already have a - // surface, let the client use that, but don't create new surface at this point. - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); - winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } else { - if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); - - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_" - + win.mAttrs.getTitle()); - outSurfaceControl.release(); - } finally { + if (outSurfaceControl != null) { + if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) { + // We already told the client to go invisible, but the message may not be + // handled yet, or it might want to draw a last frame. If we already have a + // surface, let the client use that, but don't create new surface at this + // point. + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); + winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } else { + if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); + + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_" + + win.mAttrs.getTitle()); + outSurfaceControl.release(); + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } } @@ -2534,20 +2547,16 @@ public class WindowManagerService extends IWindowManager.Stub win.mResizedWhileGone = false; } - win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration, - false /* useLatestConfig */, shouldRelayout); + if (outFrames != null && outMergedConfiguration != null) { + win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration, + false /* useLatestConfig */, shouldRelayout); - // Set resize-handled here because the values are sent back to the client. - win.onResizeHandled(); + // Set resize-handled here because the values are sent back to the client. + win.onResizeHandled(); + } - outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal()); - if (DEBUG) { - Slog.v(TAG_WM, "Relayout given client " + client.asBinder() - + ", requestedWidth=" + requestedWidth - + ", requestedHeight=" + requestedHeight - + ", viewVisibility=" + viewVisibility - + "\nRelayout returning frame=" + outFrames.frame - + ", surface=" + outSurfaceControl); + if (outInsetsState != null) { + outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal()); } ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b", @@ -2558,14 +2567,16 @@ public class WindowManagerService extends IWindowManager.Stub } win.mInRelayout = false; - if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE - && (win.mSyncSeqId > win.mLastSeqIdSentToRelayout)) { - win.markRedrawForSyncReported(); - - win.mLastSeqIdSentToRelayout = win.mSyncSeqId; - outSyncIdBundle.putInt("seqid", win.mSyncSeqId); - } else { - outSyncIdBundle.putInt("seqid", -1); + if (outSyncIdBundle != null) { + final int maybeSyncSeqId; + if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE + && win.mSyncSeqId > lastSyncSeqId) { + maybeSyncSeqId = win.mSyncSeqId; + win.markRedrawForSyncReported(); + } else { + maybeSyncSeqId = -1; + } + outSyncIdBundle.putInt("seqid", maybeSyncSeqId); } if (configChanged) { @@ -2574,7 +2585,9 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.sendNewConfiguration(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - getInsetsSourceControls(win, outActiveControls); + if (outActiveControls != null) { + getInsetsSourceControls(win, outActiveControls); + } } Binder.restoreCallingIdentity(origId); @@ -8202,6 +8215,7 @@ public class WindowManagerService extends IWindowManager.Stub final String requestWindowName; final String imeControlTargetName; final String imeLayerTargetName; + final String imeSurfaceParentName; synchronized (mGlobalLock) { final WindowState focusedWin = mWindowMap.get(focusedToken); focusedWindowName = focusedWin != null ? focusedWin.getName() : "null"; @@ -8218,15 +8232,17 @@ public class WindowManagerService extends IWindowManager.Stub } final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING); imeLayerTargetName = target != null ? target.getWindow().getName() : "null"; + final SurfaceControl imeParent = dc.mInputMethodSurfaceParent; + imeSurfaceParentName = imeParent != null ? imeParent.toString() : "null"; if (show) { dc.onShowImeRequested(); } } else { - imeControlTargetName = imeLayerTargetName = "no-display"; + imeControlTargetName = imeLayerTargetName = imeSurfaceParentName = "no-display"; } } return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName, - imeLayerTargetName); + imeLayerTargetName, imeSurfaceParentName); } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 283010ea5767..d222a566bd9d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -389,7 +389,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * examine the git commit message introducing this comment and variable.2 */ int mSyncSeqId = 0; - int mLastSeqIdSentToRelayout = 0; /** The last syncId associated with a prepareSync or 0 when no sync is active. */ int mPrepareSyncSeqId = 0; @@ -425,6 +424,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean mHaveFrame; boolean mObscured; + int mRelayoutSeq = -1; int mLayoutSeq = -1; /** @@ -1349,29 +1349,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final WindowFrames windowFrames = mWindowFrames; mTmpRect.set(windowFrames.mParentFrame); - if (LOCAL_LAYOUT) { - windowFrames.mCompatFrame.set(clientWindowFrames.frame); - - windowFrames.mFrame.set(clientWindowFrames.frame); - windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame); - windowFrames.mParentFrame.set(clientWindowFrames.parentFrame); - if (mGlobalScale != 1f) { - // The frames sent from the client need to be adjusted to the real coordinate space. - windowFrames.mFrame.scale(mGlobalScale); - windowFrames.mDisplayFrame.scale(mGlobalScale); - windowFrames.mParentFrame.scale(mGlobalScale); - } - } else { - windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame); - windowFrames.mParentFrame.set(clientWindowFrames.parentFrame); - windowFrames.mFrame.set(clientWindowFrames.frame); + windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame); + windowFrames.mParentFrame.set(clientWindowFrames.parentFrame); + windowFrames.mFrame.set(clientWindowFrames.frame); - windowFrames.mCompatFrame.set(windowFrames.mFrame); - if (mInvGlobalScale != 1f) { - // Also, the scaled frame that we report to the app needs to be adjusted to be in - // its coordinate space. - windowFrames.mCompatFrame.scale(mInvGlobalScale); - } + windowFrames.mCompatFrame.set(windowFrames.mFrame); + if (mInvGlobalScale != 1f) { + // Also, the scaled frame that we report to the app needs to be adjusted to be in + // its coordinate space. + windowFrames.mCompatFrame.scale(mInvGlobalScale); } windowFrames.setParentFrameWasClippedByDisplayCutout( clientWindowFrames.isParentFrameClippedByDisplayCutout); @@ -1415,13 +1401,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSourceFrame(windowFrames.mFrame); - if (LOCAL_LAYOUT) { - if (!mHaveFrame) { - // The first frame should not be considered as moved. - updateLastFrames(); - } - } - if (mActivityRecord != null && !mIsChildWindow) { mActivityRecord.layoutLetterbox(this); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java index 57a80131a733..427c3b3409d2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java @@ -100,7 +100,9 @@ public class BatteryControllerTest { // Capture the listeners. ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - mFlexibilityController = new FlexibilityController(mJobSchedulerService); + + mFlexibilityController = + new FlexibilityController(mJobSchedulerService, mock(PrefetchController.class)); mBatteryController = new BatteryController(mJobSchedulerService, mFlexibilityController); verify(mContext).registerReceiver(receiverCaptor.capture(), diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 7a70e7a09411..953a72d5085c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -116,8 +116,8 @@ public class ConnectivityControllerTest { LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - - mFlexibilityController = new FlexibilityController(mService); + mFlexibilityController = + new FlexibilityController(mService, mock(PrefetchController.class)); // Freeze the clocks at this moment in time JobSchedulerService.sSystemClock = 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 6d9f48dbe35c..1b39add3b421 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 @@ -16,17 +16,25 @@ package com.android.server.job.controllers; +import static android.app.job.JobInfo.BIAS_FOREGROUND_SERVICE; +import static android.app.job.JobInfo.BIAS_TOP_APP; import static android.app.job.JobInfo.NETWORK_TYPE_ANY; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 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.FcConstants.KEY_FLEXIBILITY_ENABLED; +import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS; +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.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -67,12 +75,12 @@ import java.util.concurrent.Executor; public class FlexibilityControllerTest { private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; private static final int SOURCE_USER_ID = 0; - + private static final long FROZEN_TIME = 100L; private MockitoSession mMockingSession; private FlexibilityController mFlexibilityController; private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; private JobStore mJobStore; - private FlexibilityController.FcConstants mFcConstants; + private FlexibilityController.FcConfig mFcConfig; @Mock private AlarmManager mAlarmManager; @@ -80,6 +88,8 @@ public class FlexibilityControllerTest { private Context mContext; @Mock private JobSchedulerService mJobSchedulerService; + @Mock + private PrefetchController mPrefetchController; @Before public void setup() { @@ -117,13 +127,15 @@ public class FlexibilityControllerTest { .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Freeze the clocks at a moment in time JobSchedulerService.sSystemClock = - Clock.fixed(Instant.ofEpochMilli(100L), ZoneOffset.UTC); + Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC); JobSchedulerService.sElapsedRealtimeClock = - Clock.fixed(Instant.ofEpochMilli(100L), ZoneOffset.UTC); + Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC); // Initialize real objects. - mFlexibilityController = new FlexibilityController(mJobSchedulerService); - mFcConstants = mFlexibilityController.getFcConstants(); + mFlexibilityController = new FlexibilityController(mJobSchedulerService, + mPrefetchController); + mFcConfig = mFlexibilityController.getFcConfig(); + setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L); setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true); } @@ -138,7 +150,25 @@ public class FlexibilityControllerTest { mDeviceConfigPropertiesBuilder.setBoolean(key, val); synchronized (mFlexibilityController.mLock) { mFlexibilityController.prepareForUpdatedConstantsLocked(); - mFcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + mFlexibilityController.onConstantsUpdatedLocked(); + } + } + + private void setDeviceConfigLong(String key, Long val) { + mDeviceConfigPropertiesBuilder.setLong(key, val); + synchronized (mFlexibilityController.mLock) { + mFlexibilityController.prepareForUpdatedConstantsLocked(); + mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); + mFlexibilityController.onConstantsUpdatedLocked(); + } + } + + private void setDeviceConfigString(String key, String val) { + mDeviceConfigPropertiesBuilder.setString(key, val); + synchronized (mFlexibilityController.mLock) { + mFlexibilityController.prepareForUpdatedConstantsLocked(); + mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key); mFlexibilityController.onConstantsUpdatedLocked(); } } @@ -149,76 +179,277 @@ public class FlexibilityControllerTest { private JobStatus createJobStatus(String testTag, JobInfo.Builder job) { JobInfo jobInfo = job.build(); - return JobStatus.createFromJobInfo( + JobStatus js = JobStatus.createFromJobInfo( jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, testTag); + js.enqueueTime = FROZEN_TIME; + return js; } + /** + * Tests that the there are equally many percents to drop constraints as there are constraints + */ @Test - public void testGetNextConstraintDropTimeElapsed() { + public void testDefaultVariableValues() { + assertEquals(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS, + mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length + ); + } + + @Test + public void testOnConstantsUpdated_DefaultFlexibility() { + JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0)); + assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); + setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false); + assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); + setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true); + assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js)); + } + + @Test + public void testOnConstantsUpdated_DeadlineProximity() { + JobStatus js = createJobStatus("testDeadlineProximityConfig", createJob(0)); + setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, Long.MAX_VALUE); + mFlexibilityController.mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js); + assertEquals(0, js.getNumRequiredFlexibleConstraints()); + } + + @Test + public void testOnConstantsUpdated_FallbackDeadline() { + JobStatus js = createJobStatus("testFallbackDeadlineConfig", createJob(0)); + assertEquals(DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L)); + setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 100L); + assertEquals(100L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L)); + } + + @Test + public void testOnConstantsUpdated_PercentsToDropConstraints() { + JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L); + JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb); + assertEquals(150L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20,30,40"); + assertArrayEquals( + mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, + new int[] {10, 20, 30, 40}); + assertEquals(110L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + js.adjustNumRequiredFlexibleConstraints(-1); + assertEquals(120L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + js.adjustNumRequiredFlexibleConstraints(-1); + assertEquals(130L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + } + + @Test + public void testOnConstantsUpdated_PercentsToDropConstraintsInvalidValues() { + JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L); + JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb); + js.enqueueTime = 100L; + assertEquals(150L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20a,030,40"); + assertEquals(150L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,40"); + assertEquals(150L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,40,10,40"); + assertEquals(150L, + mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); + } + + @Test + public void testGetNextConstraintDropTimeElapsedLocked() { long nextTimeToDropNumConstraints; // no delay, deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000); JobStatus js = createJobStatus("time", jb); - js.enqueueTime = 100L; - assertEquals(0, js.getEarliestRunTime()); - assertEquals(1100L, js.getLatestRunTimeElapsed()); - assertEquals(100L, js.enqueueTime); + assertEquals(JobStatus.NO_EARLIEST_RUNTIME, js.getEarliestRunTime()); + assertEquals(1000 + FROZEN_TIME, js.getLatestRunTimeElapsed()); + assertEquals(FROZEN_TIME, js.enqueueTime); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(600L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(700L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(800L, nextTimeToDropNumConstraints); // delay, no deadline jb = createJob(0).setMinimumLatency(800000L); js = createJobStatus("time", jb); - js.enqueueTime = 100L; - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(130400100, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(156320100L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(182240100L, nextTimeToDropNumConstraints); // no delay, no deadline jb = createJob(0); js = createJobStatus("time", jb); - js.enqueueTime = 100L; - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(129600100, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(155520100L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(181440100L, nextTimeToDropNumConstraints); // delay, deadline jb = createJob(0).setOverrideDeadline(1100).setMinimumLatency(100); js = createJobStatus("time", jb); - js.enqueueTime = 100L; - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(700L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(800L, nextTimeToDropNumConstraints); js.adjustNumRequiredFlexibleConstraints(-1); - nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js); + nextTimeToDropNumConstraints = mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js); assertEquals(900L, nextTimeToDropNumConstraints); } @Test + public void testCurPercent() { + long deadline = 1000; + JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline); + JobStatus js = createJobStatus("time", jb); + + assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + assertEquals(deadline + FROZEN_TIME, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME)); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME), ZoneOffset.UTC); + assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC); + assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME), ZoneOffset.UTC); + assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC); + long delay = 100; + deadline = 1100; + jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay); + js = createJobStatus("time", jb); + + assertEquals(FROZEN_TIME + delay, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + assertEquals(deadline + FROZEN_TIME, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME + delay)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME + delay), ZoneOffset.UTC); + + assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC); + assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME + delay), ZoneOffset.UTC); + assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + } + + @Test + public void testGetLifeCycleBeginningElapsedLocked_prefetch() { + // prefetch with lifecycle + when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(700L); + JobInfo.Builder jb = createJob(0).setPrefetch(true); + JobStatus js = createJobStatus("time", jb); + when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(900L); + assertEquals(900L - 700L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + // prefetch with enqueue + jb = createJob(0).setPrefetch(true); + js = createJobStatus("time", jb); + assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + // prefetch with delay + jb = createJob(0).setPrefetch(true).setMinimumLatency(200); + js = createJobStatus("time", jb); + assertEquals(200 + FROZEN_TIME, js.getEarliestRunTime()); + assertEquals(js.getEarliestRunTime(), + mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + // prefetch without estimate + mFlexibilityController.mPrefetchLifeCycleStart + .add(js.getUserId(), js.getSourcePackageName(), 500L); + when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(Long.MAX_VALUE); + jb = createJob(0).setPrefetch(true); + js = createJobStatus("time", jb); + assertEquals(500L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + } + + @Test + public void testGetLifeCycleBeginningElapsedLocked_nonPrefetch() { + // delay + long delay = 100; + JobInfo.Builder jb = createJob(0).setMinimumLatency(delay); + JobStatus js = createJobStatus("time", jb); + assertEquals(delay + FROZEN_TIME, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + // no delay + jb = createJob(0); + js = createJobStatus("time", jb); + assertEquals(FROZEN_TIME, + mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + } + + @Test + public void testGetLifeCycleEndElapsedLocked_prefetch() { + // prefetch no estimate + JobInfo.Builder jb = createJob(0).setPrefetch(true); + JobStatus js = createJobStatus("time", jb); + when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0)); + // prefetch with estimate + jb = createJob(0).setPrefetch(true); + js = createJobStatus("time", jb); + when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(1000L); + assertEquals(1000L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0)); + } + @Test + public void testGetLifeCycleEndElapsedLocked_nonPrefetch() { + // deadline + JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L); + JobStatus js = createJobStatus("time", jb); + assertEquals(1000L + FROZEN_TIME, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0)); + // no deadline + jb = createJob(0); + js = createJobStatus("time", jb); + assertEquals(100L + DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, 100L)); + } + + @Test public void testWontStopJobFromRunning() { JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101)); // Stop satisfied constraints from causing a false positive. @@ -233,8 +464,9 @@ public class FlexibilityControllerTest { public void testFlexibilityTracker() { FlexibilityController.FlexibilityTracker flexTracker = mFlexibilityController.new - FlexibilityTracker(FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS); + FlexibilityTracker(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS); + assertEquals(4, flexTracker.size()); JobStatus[] jobs = new JobStatus[4]; JobInfo.Builder jb; for (int i = 0; i < jobs.length; i++) { @@ -282,6 +514,29 @@ public class FlexibilityControllerTest { assertEquals(0, trackedJobs.get(1).size()); assertEquals(1, trackedJobs.get(2).size()); assertEquals(0, trackedJobs.get(3).size()); + + flexTracker.resetJobNumDroppedConstraints(jobs[0]); + assertEquals(0, trackedJobs.get(0).size()); + assertEquals(0, trackedJobs.get(1).size()); + assertEquals(2, trackedJobs.get(2).size()); + assertEquals(0, trackedJobs.get(3).size()); + + flexTracker.adjustJobsRequiredConstraints(jobs[0], -2); + + assertEquals(1, trackedJobs.get(0).size()); + assertEquals(0, trackedJobs.get(1).size()); + assertEquals(1, trackedJobs.get(2).size()); + assertEquals(0, trackedJobs.get(3).size()); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2) + + HOUR_IN_MILLIS), ZoneOffset.UTC); + + flexTracker.resetJobNumDroppedConstraints(jobs[0]); + assertEquals(0, trackedJobs.get(0).size()); + assertEquals(1, trackedJobs.get(1).size()); + assertEquals(1, trackedJobs.get(2).size()); + assertEquals(0, trackedJobs.get(3).size()); } } @@ -303,14 +558,6 @@ public class FlexibilityControllerTest { } @Test - public void testExceptions_Prefetch() { - JobInfo.Builder jb = createJob(0); - jb.setPrefetch(true); - JobStatus js = createJobStatus("testExceptions_Prefetch", jb); - assertFalse(js.hasFlexibilityConstraint()); - } - - @Test public void testExceptions_NoFlexibleConstraints() { JobInfo.Builder jb = createJob(0); jb.setRequiresDeviceIdle(true); @@ -381,7 +628,7 @@ public class FlexibilityControllerTest { @Test public void testSetConstraintSatisfied_Jobs() { JobInfo.Builder jb; - int[] constraintPermutations = { + int[] constraintCombinations = { CONSTRAINT_IDLE & CONSTRAINT_CHARGING & CONSTRAINT_BATTERY_NOT_LOW, CONSTRAINT_IDLE & CONSTRAINT_BATTERY_NOT_LOW, CONSTRAINT_IDLE & CONSTRAINT_CHARGING, @@ -393,9 +640,9 @@ public class FlexibilityControllerTest { }; int constraints; - for (int i = 0; i < constraintPermutations.length; i++) { + for (int i = 0; i < constraintCombinations.length; i++) { jb = createJob(i); - constraints = constraintPermutations[i]; + constraints = constraintCombinations[i]; jb.setRequiresDeviceIdle((constraints & CONSTRAINT_IDLE) != 0); jb.setRequiresBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0); jb.setRequiresCharging((constraints & CONSTRAINT_CHARGING) != 0); @@ -410,8 +657,8 @@ public class FlexibilityControllerTest { assertEquals(0, mFlexibilityController.mSatisfiedFlexibleConstraints); - for (int i = 0; i < constraintPermutations.length; i++) { - constraints = constraintPermutations[i]; + for (int i = 0; i < constraintCombinations.length; i++) { + constraints = constraintCombinations[i]; mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, (constraints & CONSTRAINT_CHARGING) != 0); mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, @@ -427,6 +674,130 @@ public class FlexibilityControllerTest { } } + @Test + public void testResetJobNumDroppedConstraints() { + JobInfo.Builder jb = createJob(22).setOverrideDeadline(100L); + JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb); + js.adjustNumRequiredFlexibleConstraints(3); + + mFlexibilityController.mFlexibilityTracker.add(js); + + assertEquals(3, js.getNumRequiredFlexibleConstraints()); + assertEquals(0, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(155L), ZoneOffset.UTC); + + mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1); + + assertEquals(2, js.getNumRequiredFlexibleConstraints()); + assertEquals(1, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); + + mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js); + + assertEquals(2, js.getNumRequiredFlexibleConstraints()); + assertEquals(1, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size()); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(140L), ZoneOffset.UTC); + + mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js); + + assertEquals(3, js.getNumRequiredFlexibleConstraints()); + assertEquals(0, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(175), ZoneOffset.UTC); + + mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js); + + assertEquals(0, js.getNumRequiredFlexibleConstraints()); + assertEquals(3, js.getNumDroppedFlexibleConstraints()); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(165L), ZoneOffset.UTC); + + mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js); + + assertEquals(1, js.getNumRequiredFlexibleConstraints()); + assertEquals(2, js.getNumDroppedFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size()); + } + + @Test + public void testOnPrefetchCacheUpdated() { + ArraySet<JobStatus> jobs = new ArraySet<JobStatus>(); + JobInfo.Builder jb = createJob(22).setPrefetch(true); + JobStatus js = createJobStatus("onPrefetchCacheUpdated", jb); + jobs.add(js); + when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(7 * HOUR_IN_MILLIS); + when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn( + 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS); + + mFlexibilityController.maybeStartTrackingJobLocked(js, null); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(150L), ZoneOffset.UTC); + + mFlexibilityController.mPrefetchChangedListener.onPrefetchCacheUpdated( + jobs, js.getUserId(), js.getSourcePackageName(), Long.MAX_VALUE, + 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS); + + assertEquals(150L, + (long) mFlexibilityController.mPrefetchLifeCycleStart + .get(js.getSourceUserId(), js.getSourcePackageName())); + assertEquals(150L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); + assertEquals(1150L, + mFlexibilityController.getLifeCycleEndElapsedLocked(js, 150L)); + assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js)); + assertEquals(650L, mFlexibilityController + .getNextConstraintDropTimeElapsedLocked(js)); + assertEquals(3, js.getNumRequiredFlexibleConstraints()); + assertEquals(1, mFlexibilityController + .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size()); + } + + /** + * The beginning of a lifecycle for prefetch jobs includes the cached maximum of the last time + * the estimated launch time was updated and the last time the app was opened. + * When the UID bias updates it means the app might have been opened. + * This tests that the cached value is updated properly. + */ + @Test + public void testUidUpdatesLifeCycle() { + JobInfo.Builder jb = createJob(0).setPrefetch(true); + JobStatus js = createJobStatus("uidTest", jb); + mFlexibilityController.maybeStartTrackingJobLocked(js, null); + mJobStore.add(js); + + final ArraySet<String> pkgs = new ArraySet<>(); + pkgs.add(js.getSourcePackageName()); + when(mJobSchedulerService.getPackagesForUidLocked(js.getUid())).thenReturn(pkgs); + + setUidBias(js.getUid(), BIAS_TOP_APP); + setUidBias(js.getUid(), BIAS_FOREGROUND_SERVICE); + assertEquals(100L, (long) mFlexibilityController.mPrefetchLifeCycleStart + .getOrDefault(js.getSourceUserId(), js.getSourcePackageName(), 0L)); + + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(Instant.ofEpochMilli(50L), ZoneOffset.UTC); + + setUidBias(js.getUid(), BIAS_TOP_APP); + setUidBias(js.getUid(), BIAS_FOREGROUND_SERVICE); + assertEquals(100L, (long) mFlexibilityController + .mPrefetchLifeCycleStart.get(js.getSourceUserId(), js.getSourcePackageName())); + + } + private void setUidBias(int uid, int bias) { int prevBias = mJobSchedulerService.getUidBias(uid); doReturn(bias).when(mJobSchedulerService).getUidBias(uid); 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 f15e60f32fb7..df523fedc917 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 @@ -33,6 +33,7 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVI import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_DEADLINE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_DEVICE_NOT_DOZING; +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.CONSTRAINT_STORAGE_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_TIMING_DELAY; @@ -790,6 +791,83 @@ public class JobStatusTest { assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); } + @Test + public void testWouldBeReadyWithConstraint_FlexibilityDoesNotAffectReadiness() { + final JobStatus job = createJobStatus( + new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); + + markImplicitConstraintsSatisfied(job, false); + job.setFlexibilityConstraintSatisfied(0, false); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE)); + + markImplicitConstraintsSatisfied(job, true); + job.setFlexibilityConstraintSatisfied(0, false); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE)); + + markImplicitConstraintsSatisfied(job, false); + job.setFlexibilityConstraintSatisfied(0, true); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); + assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE)); + + markImplicitConstraintsSatisfied(job, true); + job.setFlexibilityConstraintSatisfied(0, true); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); + assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE)); + } + + @Test + public void testReadinessStatusWithConstraint_FlexibilityConstraint() { + final JobStatus job = createJobStatus( + new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); + job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), false); + markImplicitConstraintsSatisfied(job, true); + assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true)); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false)); + + markImplicitConstraintsSatisfied(job, false); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true)); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false)); + + job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), true); + markImplicitConstraintsSatisfied(job, true); + assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true)); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false)); + + markImplicitConstraintsSatisfied(job, false); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true)); + assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false)); + } + private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) { job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); @@ -797,7 +875,6 @@ public class JobStatusTest { sElapsedRealtimeClock.millis(), isSatisfied, false); job.setBackgroundNotRestrictedConstraintSatisfied( sElapsedRealtimeClock.millis(), isSatisfied, false); - job.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); } private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java index bcdfc3500e94..7242b1bf1ccb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java @@ -480,4 +480,32 @@ public class PrefetchControllerTest { sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS); } + + @Test + public void testRegisterOnPrefetchChangedListener() { + when(mUsageStatsManagerInternal + .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID)) + .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS); + // Needs to get wrapped in an array to get accessed by an inner class. + final boolean[] onPrefetchCacheChangedCalled = new boolean[1]; + final PrefetchController.PrefetchChangedListener prefetchChangedListener = + new PrefetchController.PrefetchChangedListener() { + @Override + public void onPrefetchCacheUpdated( + ArraySet<JobStatus> jobs, int userId, String pkgName, + long prevEstimatedLaunchTime, long newEstimatedLaunchTime) { + onPrefetchCacheChangedCalled[0] = true; + } + }; + mPrefetchController.registerPrefetchChangedListener(prefetchChangedListener); + + JobStatus jobStatus = createJobStatus("testRegisterOnPrefetchChangedListener", 1); + trackJobs(jobStatus); + + mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID, + SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS); + verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any()); + + assertTrue(onPrefetchCacheChangedCalled[0]); + } } diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java index 9e986be99e95..da7664b82294 100644 --- a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -17,7 +17,10 @@ package com.android.server.tare; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.util.ArraySet; import android.util.SparseLongArray; @@ -99,7 +102,9 @@ public class AgentTrendCalculatorTest { @Before public void setUp() { - mEconomicPolicy = new MockEconomicPolicy(mock(InternalResourceService.class)); + final InternalResourceService irs = mock(InternalResourceService.class); + when(irs.isVip(anyInt(), anyString())).thenReturn(false); + mEconomicPolicy = new MockEconomicPolicy(irs); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java index 13510adbb960..d90d8b8bfac0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java @@ -68,7 +68,7 @@ public class ScribeTest { private MockitoSession mMockingSession; private Scribe mScribeUnderTest; private File mTestFileDir; - private final List<PackageInfo> mInstalledPackages = new ArrayList<>(); + private final List<InstalledPackageInfo> mInstalledPackages = new ArrayList<>(); private final List<Analyst.Report> mReports = new ArrayList<>(); @Mock @@ -455,6 +455,6 @@ public class ScribeTest { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode())); pkgInfo.applicationInfo = applicationInfo; - mInstalledPackages.add(pkgInfo); + mInstalledPackages.add(new InstalledPackageInfo(pkgInfo)); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java new file mode 100644 index 000000000000..8cd58abcdf0f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricService; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; +import android.hardware.face.IFaceService; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +public class FaceServiceRegistryTest { + + private static final int SENSOR_ID_1 = 1; + private static final int SENSOR_ID_2 = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private IFaceService mFaceService; + @Mock + private ServiceProvider mProvider1; + @Mock + private ServiceProvider mProvider2; + @Captor + private ArgumentCaptor<Integer> mIdCaptor; + @Captor + private ArgumentCaptor<Integer> mStrengthCaptor; + + private FaceSensorPropertiesInternal mProvider1Props; + private FaceSensorPropertiesInternal mProvider2Props; + private FaceServiceRegistry mRegistry; + + @Before + public void setup() { + mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1, + STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, + List.of(), FaceSensorProperties.TYPE_RGB, + true /* supportsFace Detection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresHardwareAuthToken */); + mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2, + STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FaceSensorProperties.TYPE_IR, + true /* supportsFace Detection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresHardwareAuthToken */); + + when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); + when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); + when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); + when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); + mRegistry = new FaceServiceRegistry(mFaceService, () -> mBiometricService); + } + + @Test + public void registersAllProviders() throws Exception { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); + assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); + verify(mBiometricService, times(2)).registerAuthenticator( + mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any()); + assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); + assertThat(mStrengthCaptor.getAllValues()) + .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); + } + + @Test + public void getsProviderById() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); + assertThat(mRegistry.getProviderForSensor(500)).isNull(); + } + + @Test + public void getsSingleProvider() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviders()).containsExactly(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + } + + @Test + public void registersListenerBeforeAllRegistered() { + final List<FaceSensorPropertiesInternal> all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FaceSensorPropertiesInternal> sensors) { + all.addAll(sensors); + } + }); + + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } + + @Test + public void registersListenerAfterAllRegistered() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + final List<FaceSensorPropertiesInternal> all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FaceSensorPropertiesInternal> sensors) { + all.addAll(sensors); + } + }); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java index 5f88c99b1d1e..0e30782eaece 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -24,38 +26,73 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricStateListener; +import android.hardware.biometrics.SensorPropertiesInternal; +import android.os.UserManager; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.BiometricServiceProvider; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.EnrollClient; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; @Presubmit @SmallTest public class BiometricStateCallbackTest { - private BiometricStateCallback mCallback; + private static final int USER_ID = 10; + private static final int SENSOR_ID = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + private BiometricStateCallback<FakeProvider, SensorPropertiesInternal> mCallback; @Mock - BiometricStateListener mBiometricStateListener; + private UserManager mUserManager; + @Mock + private BiometricStateListener mBiometricStateListener; + @Mock + private FakeProvider mFakeProvider; + + private SensorPropertiesInternal mFakeProviderProps; @Before public void setup() { - MockitoAnnotations.initMocks(this); - - mCallback = new BiometricStateCallback(); + mFakeProviderProps = new SensorPropertiesInternal(SENSOR_ID, STRENGTH_STRONG, + 5 /* maxEnrollmentsPerUser */, List.of(), + false /* resetLockoutRequiresHardwareAuthToken */, + false /* resetLockoutRequiresChallenge */); + when(mFakeProvider.getSensorProperties()).thenReturn(List.of(mFakeProviderProps)); + when(mFakeProvider.getSensorProperties(eq(SENSOR_ID))).thenReturn(mFakeProviderProps); + when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true); + when(mUserManager.getAliveUsers()).thenReturn( + List.of(new UserInfo(USER_ID, "name", 0))); + + mCallback = new BiometricStateCallback<>(mUserManager); mCallback.registerBiometricStateListener(mBiometricStateListener); } @Test + public void startNotifiesEnrollments() { + mCallback.start(List.of(mFakeProvider)); + + verify(mBiometricStateListener).onEnrollmentsChanged(eq(USER_ID), eq(SENSOR_ID), eq(true)); + } + + @Test public void testNoEnrollmentsToEnrollments_callbackNotified() { testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */, true /* expectCallback */, true /* expectedCallbackValue */); @@ -102,4 +139,6 @@ public class BiometricStateCallbackTest { verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(), anyBoolean()); } + + private interface FakeProvider extends BiometricServiceProvider<SensorPropertiesInternal> {} } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java new file mode 100644 index 000000000000..67d94a8f02d8 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricService; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; +import android.hardware.fingerprint.IFingerprintService; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +public class FingerprintServiceRegistryTest { + + private static final int SENSOR_ID_1 = 1; + private static final int SENSOR_ID_2 = 2; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private IFingerprintService mFingerprintService; + @Mock + private ServiceProvider mProvider1; + @Mock + private ServiceProvider mProvider2; + @Captor + private ArgumentCaptor<Integer> mIdCaptor; + @Captor + private ArgumentCaptor<Integer> mStrengthCaptor; + + private FingerprintSensorPropertiesInternal mProvider1Props; + private FingerprintSensorPropertiesInternal mProvider2Props; + private FingerprintServiceRegistry mRegistry; + + @Before + public void setup() { + mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1, + STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */, + List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + false /* resetLockoutRequiresHardwareAuthToken */); + mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2, + STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FingerprintSensorProperties.TYPE_UNKNOWN, + false /* resetLockoutRequiresHardwareAuthToken */); + + when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props)); + when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true); + when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props)); + when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true); + mRegistry = new FingerprintServiceRegistry(mFingerprintService, () -> mBiometricService); + } + + @Test + public void registersAllProviders() throws Exception { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2); + assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props); + verify(mBiometricService, times(2)).registerAuthenticator( + mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any()); + assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2); + assertThat(mStrengthCaptor.getAllValues()) + .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG); + } + + @Test + public void getsProviderById() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2); + assertThat(mRegistry.getProviderForSensor(500)).isNull(); + } + + @Test + public void getsSingleProvider() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1)); + + assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1); + assertThat(mRegistry.getProviders()).containsExactly(mProvider1); + assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1); + } + + @Test + public void registersListenerBeforeAllRegistered() { + final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + all.addAll(sensors); + } + }); + + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } + + @Test + public void registersListenerAfterAllRegistered() { + mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2)); + + final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>(); + mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + all.addAll(sensors); + } + }); + + assertThat(all).containsExactly(mProvider1Props, mProvider2Props); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java index ca3677e3b5ff..a4048a27dfb5 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java @@ -32,7 +32,8 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; -import android.os.RemoteException; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.testing.TestableContext; @@ -52,6 +53,8 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @Presubmit @SmallTest @@ -94,9 +97,12 @@ public class FingerprintServiceTest { mContext.getTestablePermissions().setPermission( USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED); + } + private void initServiceWith(String... aidlInstances) { mService = new FingerprintService(mContext, mBiometricContext, () -> mIBiometricService, + () -> aidlInstances, (fqName) -> { if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault; if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual; @@ -105,29 +111,50 @@ public class FingerprintServiceTest { } @Test - public void registerAuthenticators_defaultOnly() throws RemoteException { - mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); + public void registerAuthenticators_defaultOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualOnly() throws RemoteException { + public void registerAuthenticators_virtualOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1); - mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of()); + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } @Test - public void registerAuthenticators_virtualAlwaysWhenNoOther() throws RemoteException { - mService.registerAuthenticatorsForService(List.of(NAME_VIRTUAL), List.of()); + public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception { + initServiceWith(NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticators(List.of()); + waitForRegistration(); verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } + private void waitForRegistration() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + mService.mServiceWrapper.addAuthenticatorsRegisteredCallback( + new IFingerprintAuthenticatorsRegisteredCallback.Stub() { + @Override + public void onAllAuthenticatorsRegistered( + List<FingerprintSensorPropertiesInternal> sensors) { + latch.countDown(); + } + }); + latch.await(5, TimeUnit.SECONDS); + } + private static SensorProps createProps(int id, byte strength, byte type) { final SensorProps props = new SensorProps(); props.commonProps = new CommonProps(); diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java index e305957c6290..c4435b77628a 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java @@ -39,16 +39,23 @@ public final class BrightnessEventTest { mBrightnessEvent = new BrightnessEvent(1); mBrightnessEvent.setReason( getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER)); + mBrightnessEvent.setPhysicalDisplayId("test"); mBrightnessEvent.setLux(100.0f); + mBrightnessEvent.setFastAmbientLux(90.0f); + mBrightnessEvent.setSlowAmbientLux(85.0f); mBrightnessEvent.setPreThresholdLux(150.0f); mBrightnessEvent.setTime(System.currentTimeMillis()); + mBrightnessEvent.setInitialBrightness(25.0f); mBrightnessEvent.setBrightness(0.6f); mBrightnessEvent.setRecommendedBrightness(0.6f); mBrightnessEvent.setHbmMax(0.62f); + mBrightnessEvent.setRbcStrength(-1); mBrightnessEvent.setThermalMax(0.65f); + mBrightnessEvent.setPowerFactor(0.2f); mBrightnessEvent.setHbmMode(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); mBrightnessEvent.setFlags(0); mBrightnessEvent.setAdjustmentFlags(0); + mBrightnessEvent.setAutomaticBrightnessEnabled(true); } @Test @@ -63,12 +70,15 @@ public final class BrightnessEventTest { public void testToStringWorksAsExpected() { String actualString = mBrightnessEvent.toString(false); String expectedString = - "BrightnessEvent: disp=1, brt=0.6, rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150" - + ".0, hbmMax=0.62, hbmMode=off, thrmMax=0.65, flags=, reason=doze [ " - + "low_pwr ]"; + "BrightnessEvent: disp=1, physDisp=test, brt=0.6, initBrt=25.0, rcmdBrt=0.6," + + " preBrt=NaN, lux=100.0, fastLux=90.0, slowLux=85.0, preLux=150.0, hbmMax=0.62," + + " hbmMode=off, rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, flags=, reason=doze" + + " [ low_pwr ], autoBrightness=true"; assertEquals(actualString, expectedString); } + + private BrightnessReason getReason(int reason, int modifier) { BrightnessReason brightnessReason = new BrightnessReason(); brightnessReason.setReason(reason); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 2cf9c01a4476..28d2aa157e0a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1252,7 +1252,15 @@ public class DisplayContentTests extends WindowTestsBase { public void testComputeImeControlTarget() throws Exception { final DisplayContent dc = createNewDisplay(); dc.setRemoteInsetsController(createDisplayWindowInsetsController()); - dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app")); + dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app"); + + // Expect returning null IME control target when the focus window has not yet been the + // IME input target (e.g. IME is restarting) in fullscreen windowing mode. + dc.setImeInputTarget(null); + assertFalse(dc.mCurrentFocus.inMultiWindowMode()); + assertNull(dc.computeImeControlTarget()); + + dc.setImeInputTarget(dc.mCurrentFocus); dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState()); assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index fde6e3cc0b4f..6d33aaffdf24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -97,6 +97,7 @@ import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -1537,6 +1538,108 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) + public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) + public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test public void testSplitAspectRatioForUnresizableLandscapeApps() { // Set up a display in portrait and ignoring orientation request. int screenWidth = 1400; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 1e0e8366c385..a1310849d84f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -18,6 +18,8 @@ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; +import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN; +import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS; @@ -58,6 +60,7 @@ import static com.android.server.voiceinteraction.SoundTriggerSessionPermissions import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.attention.AttentionManagerInternal; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; @@ -186,6 +189,12 @@ final class HotwordDetectionConnection { final int mUser; final Context mContext; + @Nullable final AttentionManagerInternal mAttentionManagerInternal; + + final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal = + this::setProximityMeters; + + volatile HotwordDetectionServiceIdentity mIdentity; private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback; private Instant mLastRestartInstant; @@ -206,6 +215,8 @@ final class HotwordDetectionConnection { private @NonNull ServiceConnection mRemoteHotwordDetectionService; private IBinder mAudioFlinger; private boolean mDebugHotwordLogging = false; + @GuardedBy("mLock") + private double mProximityMeters = PROXIMITY_UNKNOWN; HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, ComponentName serviceName, int userId, @@ -233,6 +244,10 @@ final class HotwordDetectionConnection { mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed); mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked(); + mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class); + if (mAttentionManagerInternal != null) { + mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal); + } mLastRestartInstant = Instant.now(); updateStateAfterProcessStart(options, sharedMemory); @@ -397,6 +412,9 @@ final class HotwordDetectionConnection { if (mAudioFlinger != null) { mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0); } + if (mAttentionManagerInternal != null) { + mAttentionManagerInternal.onStopProximityUpdates(mProximityCallbackInternal); + } } void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) { @@ -464,6 +482,7 @@ final class HotwordDetectionConnection { mSoftwareCallback.onError(); return; } + saveProximityMetersToBundle(result); mSoftwareCallback.onDetected(result, null, null); if (result != null) { Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result) @@ -591,6 +610,7 @@ final class HotwordDetectionConnection { externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION); return; } + saveProximityMetersToBundle(result); externalCallback.onKeyphraseDetected(recognitionEvent, result); if (result != null) { Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result) @@ -1159,6 +1179,20 @@ final class HotwordDetectionConnection { }); } + private void saveProximityMetersToBundle(HotwordDetectedResult result) { + synchronized (mLock) { + if (result != null && mProximityMeters != PROXIMITY_UNKNOWN) { + result.getExtras().putDouble(EXTRA_PROXIMITY_METERS, mProximityMeters); + } + } + } + + private void setProximityMeters(double proximityMeters) { + synchronized (mLock) { + mProximityMeters = proximityMeters; + } + } + private static void bestEffortClose(Closeable... closeables) { for (Closeable closeable : closeables) { bestEffortClose(closeable); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 4d18dfe6a62c..5ad4edf71075 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -39,6 +39,7 @@ import android.service.carrier.CarrierService; import android.telecom.TelecomManager; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.data.ApnSetting; +import android.telephony.data.DataCallResponse; import android.telephony.gba.TlsParams; import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; @@ -1125,6 +1126,27 @@ public class CarrierConfigManager { public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int"; /** + * The data call retry configuration for different types of APN. + * @hide + */ + public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS = + "carrier_data_call_retry_config_strings"; + + /** + * Delay in milliseconds between trying APN from the pool + * @hide + */ + public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG = + "carrier_data_call_apn_delay_default_long"; + + /** + * Faster delay in milliseconds between trying APN from the pool + * @hide + */ + public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG = + "carrier_data_call_apn_delay_faster_long"; + + /** * Delay in milliseconds for retrying APN after disconnect * @hide */ @@ -1132,6 +1154,21 @@ public class CarrierConfigManager { "carrier_data_call_apn_retry_after_disconnect_long"; /** + * The maximum times for telephony to retry data setup on the same APN requested by + * network through the data setup response retry timer + * {@link DataCallResponse#getRetryDurationMillis()}. This is to prevent that network keeps + * asking device to retry data setup forever and causes power consumption issue. For infinite + * retring same APN, configure this as 2147483647 (i.e. {@link Integer#MAX_VALUE}). + * + * Note if network does not suggest any retry timer, frameworks uses the retry configuration + * from {@link #KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS}, and the maximum retry times could + * be configured there. + * @hide + */ + public static final String KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT = + "carrier_data_call_retry_network_requested_max_count_int"; + + /** * Data call setup permanent failure causes by the carrier */ public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = @@ -1151,6 +1188,19 @@ public class CarrierConfigManager { "carrier_metered_roaming_apn_types_strings"; /** + * APN types that are not allowed on cellular + * @hide + */ + public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wwan_disallowed_apn_types_string_array"; + + /** + * APN types that are not allowed on IWLAN + * @hide + */ + public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wlan_disallowed_apn_types_string_array"; + /** * CDMA carrier ERI (Enhanced Roaming Indicator) file name * @hide */ @@ -8369,6 +8419,7 @@ public class CarrierConfigManager { * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s, * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries. * + * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS * @hide */ public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY = @@ -8770,13 +8821,27 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500); + sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{ + "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000," + + "320000:5000,640000:5000,1280000:5000,1800000:5000", + "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000," + + "320000:5000,640000:5000,1280000:5000,1800000:5000", + "ims:max_retries=10, 5000, 5000, 5000", + "others:max_retries=3, 5000, 5000, 5000"}); + sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000); + sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000); sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000); + sDefaults.putInt(KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 3); sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml"); sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200); sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS, new String[]{"default", "mms", "dun", "supl"}); sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, new String[]{"default", "mms", "dun", "supl"}); + sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); + sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY, new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A, diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 3ed87e1b9fe2..f794a7971343 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -24,7 +24,6 @@ import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_5.ApnTypes; import android.net.Uri; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; @@ -964,7 +963,7 @@ public class ApnSetting implements Parcelable { ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); } int mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V4)); - if (mtuV4 == -1) { + if (mtuV4 == UNSET_MTU) { mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index da1ffcdea812..0ce6b14ce3b0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2510,6 +2510,9 @@ interface ITelephony { CellIdentity getLastKnownCellIdentity(int subId, String callingPackage, String callingFeatureId); + /** Check if telephony new data stack is enabled. */ + boolean isUsingNewDataStack(); + /** * @return true if the modem service is set successfully, false otherwise. */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index e138d332bb17..be7fb7315955 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -38,7 +38,10 @@ abstract class BaseTest @JvmOverloads constructor( ) { init { testSpec.setIsTablet( - WindowManagerStateHelper(instrumentation).currentState.wmState.isTablet + WindowManagerStateHelper( + instrumentation, + clearCacheAfterParsing = false + ).currentState.wmState.isTablet ) tapl.setExpectedRotationCheckEnabled(true) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 75900df978df..d08cb5525d5e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -26,4 +26,9 @@ import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule * @param rotation New device rotation */ fun Flicker.setRotation(rotation: Int) = - ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper) + ChangeDisplayOrientationRule.setRotation( + rotation, + instrumentation, + clearCacheAfterParsing = false, + wmHelper = wmHelper +) diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index c4cb33da4a6d..44265511ee50 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -123,16 +123,27 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } boolean found = false; + boolean remountSystem = false; + boolean remountVendor = false; for (String file : files) { CommandResult result = getDevice().executeShellV2Command("ls " + file); if (result.getStatus() == CommandStatus.SUCCESS) { found = true; - break; + if (file.startsWith("/system")) { + remountSystem = true; + } else if (file.startsWith("/vendor")) { + remountVendor = true; + } } } if (found) { - getDevice().remountSystemWritable(); + if (remountSystem) { + getDevice().remountSystemWritable(); + } + if (remountVendor) { + getDevice().remountVendorWritable(); + } for (String file : files) { getDevice().executeShellCommand("rm -rf " + file); } @@ -150,7 +161,11 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { if (!getDevice().isAdbRoot()) { getDevice().enableAdbRoot(); } - getDevice().remountSystemWritable(); + if ("system".equals(partition)) { + getDevice().remountSystemWritable(); + } else if ("vendor".equals(partition)) { + getDevice().remountVendorWritable(); + } assertTrue(getDevice().pushFile(apex, "/" + partition + "/apex/" + fileName)); } @@ -158,7 +173,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { if (!getDevice().isAdbRoot()) { getDevice().enableAdbRoot(); } - getDevice().remountSystemWritable(); + getDevice().remountVendorWritable(); File file = File.createTempFile("test-vendor-apex-allow-list", ".xml"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { final String fmt = diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index fecc7b3cbf37..d02fd83df6af 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -1028,7 +1028,6 @@ int doDump(Bundle* bundle) // These permissions are required by services implementing services // the system binds to (IME, Accessibility, PrintServices, etc.) bool hasBindDeviceAdminPermission = false; - bool hasBindInputMethodPermission = false; bool hasBindAccessibilityServicePermission = false; bool hasBindPrintServicePermission = false; bool hasBindNfcServicePermission = false; @@ -1757,7 +1756,6 @@ int doDump(Bundle* bundle) hasMetaHostPaymentCategory = false; hasMetaOffHostPaymentCategory = false; hasBindDeviceAdminPermission = false; - hasBindInputMethodPermission = false; hasBindAccessibilityServicePermission = false; hasBindPrintServicePermission = false; hasBindNfcServicePermission = false; @@ -1871,9 +1869,7 @@ int doDump(Bundle* bundle) String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR, &error); if (error == "") { - if (permission == "android.permission.BIND_INPUT_METHOD") { - hasBindInputMethodPermission = true; - } else if (permission == + if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { hasBindAccessibilityServicePermission = true; } else if (permission == diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index b9de11b0026b..47750fc11a6e 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2970,14 +2970,6 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& } e->setNameIndex(keyStrings.add(e->getName(), true)); - // If this entry has no values for other configs, - // and is the default config, then it is special. Otherwise - // we want to add it with the config info. - ConfigDescription* valueConfig = NULL; - if (N != 1 || config == nullConfig) { - valueConfig = &config; - } - status_t err = e->prepareFlatten(&valueStrings, this, &configTypeName, &config); if (err != NO_ERROR) { diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt index d19f4cceeaaf..12f3a1659313 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt @@ -19,6 +19,8 @@ package com.google.android.lint import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor import com.android.tools.lint.detector.api.CURRENT_API +import com.google.android.lint.aidl.EnforcePermissionDetector +import com.google.android.lint.aidl.ManualPermissionCheckDetector import com.google.android.lint.parcel.SaferParcelChecker import com.google.auto.service.AutoService @@ -36,6 +38,7 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() { CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED, EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION, EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION, + ManualPermissionCheckDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION, SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS ) diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt new file mode 100644 index 000000000000..82eb8ed8f621 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt @@ -0,0 +1,36 @@ +/* + * 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.google.android.lint + +import com.google.android.lint.model.Method + +const val CLASS_STUB = "Stub" +const val CLASS_CONTEXT = "android.content.Context" +const val CLASS_ACTIVITY_MANAGER_SERVICE = "com.android.server.am.ActivityManagerService" +const val CLASS_ACTIVITY_MANAGER_INTERNAL = "android.app.ActivityManagerInternal" + +// Enforce permission APIs +val ENFORCE_PERMISSION_METHODS = listOf( + Method(CLASS_CONTEXT, "checkPermission"), + Method(CLASS_CONTEXT, "checkCallingPermission"), + Method(CLASS_CONTEXT, "checkCallingOrSelfPermission"), + Method(CLASS_CONTEXT, "enforcePermission"), + Method(CLASS_CONTEXT, "enforceCallingPermission"), + Method(CLASS_CONTEXT, "enforceCallingOrSelfPermission"), + Method(CLASS_ACTIVITY_MANAGER_SERVICE, "checkPermission"), + Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission") +) diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt index 192dba17dd5b..48540b1da565 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt @@ -30,13 +30,13 @@ import com.android.tools.lint.detector.api.interprocedural.CallGraphResult import com.android.tools.lint.detector.api.interprocedural.searchForPaths import com.intellij.psi.PsiAnonymousClass import com.intellij.psi.PsiMethod +import java.util.LinkedList import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod import org.jetbrains.uast.UParameter import org.jetbrains.uast.USimpleNameReferenceExpression import org.jetbrains.uast.visitor.AbstractUastVisitor -import java.util.LinkedList /** * A lint checker to detect potential package visibility issues for system's APIs. APIs working @@ -362,14 +362,18 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner { name: String, matchArgument: Boolean = true, checkCaller: Boolean = false - ): this(clazz, name) { + ) : this(clazz, name) { this.matchArgument = matchArgument this.checkCaller = checkCaller } constructor( method: PsiMethod - ): this(method.containingClass?.qualifiedName ?: "", method.name) + ) : this(method.containingClass?.qualifiedName ?: "", method.name) + + constructor( + method: com.google.android.lint.model.Method + ) : this(method.clazz, method.name) } /** @@ -380,7 +384,7 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner { val typeName: String, val parameterName: String ) { - constructor(uParameter: UParameter): this( + constructor(uParameter: UParameter) : this( uParameter.type.canonicalText, uParameter.name.lowercase() ) @@ -405,19 +409,13 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner { // A valid call path list needs to contain a start node and a sink node private const val VALID_CALL_PATH_NODES_SIZE = 2 - private const val CLASS_STUB = "Stub" private const val CLASS_STRING = "java.lang.String" private const val CLASS_PACKAGE_MANAGER = "android.content.pm.PackageManager" private const val CLASS_IPACKAGE_MANAGER = "android.content.pm.IPackageManager" private const val CLASS_APPOPS_MANAGER = "android.app.AppOpsManager" - private const val CLASS_CONTEXT = "android.content.Context" private const val CLASS_BINDER = "android.os.Binder" private const val CLASS_PACKAGE_MANAGER_INTERNAL = "android.content.pm.PackageManagerInternal" - private const val CLASS_ACTIVITY_MANAGER_SERVICE = - "com.android.server.am.ActivityManagerService" - private const val CLASS_ACTIVITY_MANAGER_INTERNAL = - "android.app.ActivityManagerInternal" // Patterns of package name parameter private val PACKAGE_NAME_PATTERNS = setOf( @@ -455,16 +453,9 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner { ) // Enforce permission APIs - private val ENFORCE_PERMISSION_METHODS = listOf( - Method(CLASS_CONTEXT, "checkPermission"), - Method(CLASS_CONTEXT, "checkCallingPermission"), - Method(CLASS_CONTEXT, "checkCallingOrSelfPermission"), - Method(CLASS_CONTEXT, "enforcePermission"), - Method(CLASS_CONTEXT, "enforceCallingPermission"), - Method(CLASS_CONTEXT, "enforceCallingOrSelfPermission"), - Method(CLASS_ACTIVITY_MANAGER_SERVICE, "checkPermission"), - Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission") - ) + private val ENFORCE_PERMISSION_METHODS = + com.google.android.lint.ENFORCE_PERMISSION_METHODS + .map(PackageVisibilityDetector::Method) private val BYPASS_STUBS = listOf( "android.content.pm.IPackageDataObserver.Stub", diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt new file mode 100644 index 000000000000..8ee3763e5079 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt @@ -0,0 +1,73 @@ +/* + * 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.google.android.lint.aidl + +const val ANNOTATION_ENFORCE_PERMISSION = "android.annotation.EnforcePermission" +const val ANNOTATION_REQUIRES_NO_PERMISSION = "android.annotation.RequiresNoPermission" +const val ANNOTATION_PERMISSION_MANUALLY_ENFORCED = "android.annotation.PermissionManuallyEnforced" + +val AIDL_PERMISSION_ANNOTATIONS = listOf( + ANNOTATION_ENFORCE_PERMISSION, + ANNOTATION_REQUIRES_NO_PERMISSION, + ANNOTATION_PERMISSION_MANUALLY_ENFORCED +) + +const val IINTERFACE_INTERFACE = "android.os.IInterface" + +/** + * If a non java (e.g. c++) backend is enabled, the @EnforcePermission + * annotation cannot be used. At time of writing, the mechanism + * is not implemented for non java backends. + * TODO: b/242564874 (have lint know which interfaces have the c++ backend enabled) + * rather than hard coding this list? + */ +val EXCLUDED_CPP_INTERFACES = listOf( + "AdbTransportType", + "FingerprintAndPairDevice", + "IAdbCallback", + "IAdbManager", + "PairDevice", + "IStatsBootstrapAtomService", + "StatsBootstrapAtom", + "StatsBootstrapAtomValue", + "FixedSizeArrayExample", + "PlaybackTrackMetadata", + "RecordTrackMetadata", + "SinkMetadata", + "SourceMetadata", + "IUpdateEngineStable", + "IUpdateEngineStableCallback", + "AudioCapabilities", + "ConfidenceLevel", + "ModelParameter", + "ModelParameterRange", + "Phrase", + "PhraseRecognitionEvent", + "PhraseRecognitionExtra", + "PhraseSoundModel", + "Properties", + "RecognitionConfig", + "RecognitionEvent", + "RecognitionMode", + "RecognitionStatus", + "SoundModel", + "SoundModelType", + "Status", + "IThermalService", + "IPowerManager", + "ITunerResourceManager" +) diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt index 9f216189ad62..a415217105a8 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.google.android.lint +package com.google.android.lint.aidl import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.AnnotationInfo import com.android.tools.lint.detector.api.AnnotationOrigin import com.android.tools.lint.detector.api.AnnotationUsageInfo import com.android.tools.lint.detector.api.AnnotationUsageType -import com.android.tools.lint.detector.api.ConstantEvaluator import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.ConstantEvaluator import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Issue @@ -34,8 +34,8 @@ import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClass import com.intellij.psi.PsiMethod import org.jetbrains.uast.UAnnotation -import org.jetbrains.uast.UElement import org.jetbrains.uast.UClass +import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod /** @@ -54,12 +54,11 @@ import org.jetbrains.uast.UMethod */ class EnforcePermissionDetector : Detector(), SourceCodeScanner { - val ENFORCE_PERMISSION = "android.annotation.EnforcePermission" val BINDER_CLASS = "android.os.Binder" val JAVA_OBJECT = "java.lang.Object" override fun applicableAnnotations(): List<String> { - return listOf(ENFORCE_PERMISSION) + return listOf(ANNOTATION_ENFORCE_PERMISSION) } override fun getApplicableUastTypes(): List<Class<out UElement>> { @@ -99,8 +98,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { overriddenMethod: PsiMethod, checkEquivalence: Boolean = true ) { - val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION) - val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION) + val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) + val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) val location = context.getLocation(element) val overridingClass = overridingMethod.parent as PsiClass val overriddenClass = overriddenMethod.parent as PsiClass @@ -133,8 +132,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { extendedClass: PsiClass, checkEquivalence: Boolean = true ) { - val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION) - val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION) + val newAnnotation = newClass.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) + val extendedAnnotation = extendedClass.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) val location = context.getLocation(element) val newClassName = newClass.qualifiedName @@ -180,7 +179,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { override fun createUastHandler(context: JavaContext): UElementHandler { return object : UElementHandler() { override fun visitAnnotation(node: UAnnotation) { - if (node.qualifiedName != ENFORCE_PERMISSION) { + if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) { return } val method = node.uastParent as? UMethod diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt new file mode 100644 index 000000000000..510611161ea8 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt @@ -0,0 +1,118 @@ +/* + * 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.google.android.lint.aidl + +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Location +import com.intellij.psi.PsiVariable +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.ULiteralExpression +import org.jetbrains.uast.UQualifiedReferenceExpression +import org.jetbrains.uast.USimpleNameReferenceExpression +import org.jetbrains.uast.asRecursiveLogString + +/** + * Helper ADT class that facilitates the creation of lint auto fixes + * + * Handles "Single" permission checks that should be migrated to @EnforcePermission(...), as well as consecutive checks + * that should be migrated to @EnforcePermission(allOf={...}) + * + * TODO: handle anyOf style annotations + */ +sealed class EnforcePermissionFix { + abstract fun locations(): List<Location> + abstract fun javaAnnotationParameter(): String + + fun javaAnnotation(): String = "@$ANNOTATION_ENFORCE_PERMISSION(${javaAnnotationParameter()})" + + companion object { + fun fromCallExpression(callExpression: UCallExpression, context: JavaContext): SingleFix = + SingleFix( + getPermissionCheckLocation(context, callExpression), + getPermissionCheckArgumentValue(callExpression) + ) + + fun maybeAddManifestPrefix(permissionName: String): String = + if (permissionName.contains(".")) permissionName + else "android.Manifest.permission.$permissionName" + + /** + * Given a permission check, get its proper location + * so that a lint fix can remove the entire expression + */ + private fun getPermissionCheckLocation( + context: JavaContext, + callExpression: UCallExpression + ): + Location { + val javaPsi = callExpression.javaPsi!! + return Location.create( + context.file, + javaPsi.containingFile?.text, + javaPsi.textRange.startOffset, + // unfortunately the element doesn't include the ending semicolon + javaPsi.textRange.endOffset + 1 + ) + } + + /** + * Given a permission check and an argument, + * pull out the permission value that is being used + */ + private fun getPermissionCheckArgumentValue( + callExpression: UCallExpression, + argumentPosition: Int = 0 + ): String { + + val identifier = when ( + val argument = callExpression.valueArguments.getOrNull(argumentPosition) + ) { + is UQualifiedReferenceExpression -> when (val selector = argument.selector) { + is USimpleNameReferenceExpression -> + ((selector.resolve() as PsiVariable).computeConstantValue() as String) + + else -> throw RuntimeException( + "Couldn't resolve argument: ${selector.asRecursiveLogString()}" + ) + } + + is USimpleNameReferenceExpression -> ( + (argument.resolve() as PsiVariable).computeConstantValue() as String) + + is ULiteralExpression -> argument.value as String + + else -> throw RuntimeException( + "Couldn't resolve argument: ${argument?.asRecursiveLogString()}" + ) + } + + return identifier.substringAfterLast(".") + } + } +} + +data class SingleFix(val location: Location, val permissionName: String) : EnforcePermissionFix() { + override fun locations(): List<Location> = listOf(this.location) + override fun javaAnnotationParameter(): String = maybeAddManifestPrefix(this.permissionName) +} +data class AllOfFix(val checks: List<SingleFix>) : EnforcePermissionFix() { + override fun locations(): List<Location> = this.checks.map { it.location } + override fun javaAnnotationParameter(): String = + "allOf={${ + this.checks.joinToString(", ") { maybeAddManifestPrefix(it.permissionName) } + }}" +} diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt new file mode 100644 index 000000000000..2cea39423f1d --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt @@ -0,0 +1,199 @@ +/* + * 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.google.android.lint.aidl + +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.google.android.lint.CLASS_STUB +import com.google.android.lint.ENFORCE_PERMISSION_METHODS +import com.intellij.psi.PsiAnonymousClass +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UIfExpression +import org.jetbrains.uast.UMethod +import org.jetbrains.uast.UQualifiedReferenceExpression + +/** + * Looks for methods implementing generated AIDL interface stubs + * that can have simple permission checks migrated to + * @EnforcePermission annotations + * + * TODO: b/242564870 (enable parse and autoFix of .aidl files) + */ +@Suppress("UnstableApiUsage") +class ManualPermissionCheckDetector : Detector(), SourceCodeScanner { + override fun getApplicableUastTypes(): List<Class<out UElement?>> = + listOf(UMethod::class.java) + + override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context) + + private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() { + override fun visitMethod(node: UMethod) { + val interfaceName = getContainingAidlInterface(node) + .takeUnless(EXCLUDED_CPP_INTERFACES::contains) ?: return + val body = (node.uastBody as? UBlockExpression) ?: return + val fix = accumulateSimplePermissionCheckFixes(body) ?: return + + val javaRemoveFixes = fix.locations().map { + fix() + .replace() + .reformat(true) + .range(it) + .with("") + .autoFix() + .build() + } + + val javaAnnotateFix = fix() + .annotate(fix.javaAnnotation()) + .range(context.getLocation(node)) + .autoFix() + .build() + + val message = + "$interfaceName permission check can be converted to @EnforcePermission annotation" + + context.report( + ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION, + fix.locations().last(), + message, + fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix) + ) + } + + /** + * Walk the expressions in the method, looking for simple permission checks. + * + * If a single permission check is found at the beginning of the method, + * this should be migrated to @EnforcePermission(value). + * + * If multiple consecutive permission checks are found, + * these should be migrated to @EnforcePermission(allOf={value1, value2, ...}) + * + * As soon as something other than a permission check is encountered, stop looking, + * as some other business logic is happening that prevents an automated fix. + */ + private fun accumulateSimplePermissionCheckFixes(methodBody: UBlockExpression): + EnforcePermissionFix? { + val singleFixes = mutableListOf<SingleFix>() + for (expression in methodBody.expressions) { + singleFixes.add(getPermissionCheckFix(expression) ?: break) + } + return when (singleFixes.size) { + 0 -> null + 1 -> singleFixes[0] + else -> AllOfFix(singleFixes) + } + } + + /** + * If an expression boils down to a permission check, return + * the helper for creating a lint auto fix to @EnforcePermission + */ + private fun getPermissionCheckFix(startingExpression: UElement?): + SingleFix? { + return when (startingExpression) { + is UQualifiedReferenceExpression -> getPermissionCheckFix( + startingExpression.selector + ) + + is UIfExpression -> getPermissionCheckFix(startingExpression.condition) + + is UCallExpression -> { + return if (isPermissionCheck(startingExpression)) + EnforcePermissionFix.fromCallExpression(startingExpression, context) + else null + } + + else -> null + } + } + } + + companion object { + + private val EXPLANATION = """ + Whenever possible, method implementations of AIDL interfaces should use the @EnforcePermission + annotation to declare the permissions to be enforced. The verification code is then + generated by the AIDL compiler, which also takes care of annotating the generated java + code. + + This reduces the risk of bugs around these permission checks (that often become vulnerabilities). + It also enables easier auditing and review. + + Please migrate to an @EnforcePermission annotation. (See: go/aidl-enforce-howto) + """.trimIndent() + + @JvmField + val ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION = Issue.create( + id = "UseEnforcePermissionAnnotation", + briefDescription = "Manual permission check can be @EnforcePermission annotation", + explanation = EXPLANATION, + category = Category.SECURITY, + priority = 5, + severity = Severity.WARNING, + implementation = Implementation( + ManualPermissionCheckDetector::class.java, + Scope.JAVA_FILE_SCOPE + ), + enabledByDefault = false, // TODO: enable once b/241171714 is resolved + ) + + private fun isPermissionCheck(callExpression: UCallExpression): Boolean { + val method = callExpression.resolve() ?: return false + val className = method.containingClass?.qualifiedName + return ENFORCE_PERMISSION_METHODS.any { + it.clazz == className && it.name == method.name + } + } + + /** + * given a UMethod, determine if this method is + * an entrypoint to an interface generated by AIDL, + * returning the interface name if so + */ + fun getContainingAidlInterface(node: UMethod): String? { + if (!isInClassCalledStub(node)) return null + for (superMethod in node.findSuperMethods()) { + for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements + ?: continue) { + if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) { + return superMethod.containingClass?.name + } + } + } + return null + } + + private fun isInClassCalledStub(node: UMethod): Boolean { + (node.containingClass as? PsiAnonymousClass)?.let { + return it.baseClassReference.referenceName == CLASS_STUB + } + return node.containingClass?.extendsList?.referenceElements?.any { + it.referenceName == CLASS_STUB + } ?: false + } + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt b/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt index fce9f2b19ee0..3939b6109eaa 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt @@ -14,21 +14,13 @@ * limitations under the License. */ -package com.android.settingslib.spa.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable +package com.google.android.lint.model /** - * The Material 3 Theme for Settings. + * Data class to represent a Method */ -@Composable -fun SettingsTheme(content: @Composable () -> Unit) { - val isDarkTheme = isSystemInDarkTheme() - val colorScheme = materialColorScheme(isDarkTheme) - - MaterialTheme(colorScheme = colorScheme) { - content() +data class Method(val clazz: String, val name: String) { + override fun toString(): String { + return "$clazz#$name" } } diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt index 2cfc3fbcefcb..3c1d1e8e8a59 100644 --- a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt +++ b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.android.lint +package com.google.android.lint.aidl import com.android.tools.lint.checks.infrastructure.LintDetectorTest import com.android.tools.lint.checks.infrastructure.TestFile diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt new file mode 100644 index 000000000000..1a1c6bc77785 --- /dev/null +++ b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt @@ -0,0 +1,211 @@ +/* + * 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.google.android.lint.aidl + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue + +@Suppress("UnstableApiUsage") +class ManualPermissionCheckDetectorTest : LintDetectorTest() { + override fun getDetector(): Detector = ManualPermissionCheckDetector() + override fun getIssues(): List<Issue> = listOf( + ManualPermissionCheckDetector + .ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION + ) + + override fun lint(): TestLintTask = super.lint().allowMissingSdk() + + fun testClass() { + lint().files( + java( + """ + import android.content.Context; + import android.test.ITest; + public class Foo extends ITest.Stub { + private Context mContext; + @Override + public void test() throws android.os.RemoteException { + mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo"); + } + } + """ + ).indented(), + *stubs + ) + .run() + .expect( + """ + src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation] + mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo"); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """ + ) + .expectFixDiffs( + """ + Fix for src/Foo.java line 7: Annotate with @EnforcePermission: + @@ -5 +5 + + @android.annotation.EnforcePermission(android.Manifest.permission.READ_CONTACTS) + @@ -7 +8 + - mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo"); + """ + ) + } + + fun testAnonClass() { + lint().files( + java( + """ + import android.content.Context; + import android.test.ITest; + public class Foo { + private Context mContext; + private ITest itest = new ITest.Stub() { + @Override + public void test() throws android.os.RemoteException { + mContext.enforceCallingOrSelfPermission( + "android.Manifest.permission.READ_CONTACTS", "foo"); + } + }; + } + """ + ).indented(), + *stubs + ) + .run() + .expect( + """ + src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation] + mContext.enforceCallingOrSelfPermission( + ^ + 0 errors, 1 warnings + """ + ) + .expectFixDiffs( + """ + Fix for src/Foo.java line 8: Annotate with @EnforcePermission: + @@ -6 +6 + + @android.annotation.EnforcePermission(android.Manifest.permission.READ_CONTACTS) + @@ -8 +9 + - mContext.enforceCallingOrSelfPermission( + - "android.Manifest.permission.READ_CONTACTS", "foo"); + """ + ) + } + + fun testAllOf() { + lint().files( + java( + """ + import android.content.Context; + import android.test.ITest; + public class Foo { + private Context mContext; + private ITest itest = new ITest.Stub() { + @Override + public void test() throws android.os.RemoteException { + mContext.enforceCallingOrSelfPermission( + "android.Manifest.permission.READ_CONTACTS", "foo"); + mContext.enforceCallingOrSelfPermission( + "android.Manifest.permission.WRITE_CONTACTS", "foo"); + } + }; + } + """ + ).indented(), + *stubs + ) + .run() + .expect( + """ + src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation] + mContext.enforceCallingOrSelfPermission( + ^ + 0 errors, 1 warnings + """ + ) + .expectFixDiffs( + """ + Fix for src/Foo.java line 10: Annotate with @EnforcePermission: + @@ -6 +6 + + @android.annotation.EnforcePermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.WRITE_CONTACTS}) + @@ -8 +9 + - mContext.enforceCallingOrSelfPermission( + - "android.Manifest.permission.READ_CONTACTS", "foo"); + - mContext.enforceCallingOrSelfPermission( + - "android.Manifest.permission.WRITE_CONTACTS", "foo"); + """ + ) + } + + fun testPrecedingExpressions() { + lint().files( + java( + """ + import android.os.Binder; + import android.test.ITest; + public class Foo extends ITest.Stub { + private mContext Context; + @Override + public void test() throws android.os.RemoteException { + long uid = Binder.getCallingUid(); + mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo"); + } + } + """ + ).indented(), + *stubs + ) + .run() + .expectClean() + } + + companion object { + private val aidlStub: TestFile = java( + """ + package android.test; + public interface ITest extends android.os.IInterface { + public static abstract class Stub extends android.os.Binder implements android.test.ITest {} + public void test() throws android.os.RemoteException; + } + """ + ).indented() + + private val contextStub: TestFile = java( + """ + package android.content; + public class Context { + public void enforceCallingOrSelfPermission(String permission, String message) {} + } + """ + ).indented() + + private val binderStub: TestFile = java( + """ + package android.os; + public class Binder { + public static int getCallingUid() {} + } + """ + ).indented() + + val stubs = arrayOf(aidlStub, contextStub, binderStub) + } +} |